Difftime code

Clive D.W. Feather clive at demon.net
Fri Aug 6 05:00:38 UTC 2004

Paul Eggert said:
> Wow, that's pretty a complicated way to subtract two numbers.  :-)

Only if you want the right answer :-)

> If we can assume uintmax_t (by typedefing it on older hosts), isn't
> there a much simpler approach entirely?

No. [Note: my top_type is effectively that typedef.]

> The idea behind the sizeof test is to avoid "long double" if it's safe
> to do so, since long double is expensive on some hosts.

Is it *that* expensive? I thought you were trying to avoid rounding errors.

> #define TYPE_FLOATING(type) ((type) 0.5 != 0)
> #define TYPE_SIGNED(type) (((type) -1) < 0)
> #define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
> double
> difftime (time_t time1, time_t time0)
> {
>   if (TYPE_BIT (time_t) <= DBL_MANT_DIG

That's a conservative test I have no objection to. It's *very*
conservative, since if FLT_RADIX isn't 2 it will seriously underestimate
how big double is.

You might be better off comparing time1 and time0 with DBL_EPSILON or use
the maxLDint trick I described.

But if you're worried about efficiency, why are you doing this in floating
point when they're integers?

>       || (TYPE_FLOATING (time_t) && sizeof (time_t) != sizeof (long double)))

Okay, this proves that time_t isn't long double. Clever.

>     return (double) time1 - (double) time0;
>   if (TYPE_FLOATING (time_t))
>     return (long_double) time1 - (long_double) time0;
>   if (time1 < time0)
>     return -simple_difftime (time0, time1);
>   else
>     return simple_difftime (time1, time0);
> }

Moved here for expositional purposes:

> static double
> simple_difftime (time_t time1, time_t time0)
> {
>   if (TYPE_SIGNED (time_t))
>     return (uintmax_t) time1 - (uintmax_t) time0;
>   else
>     return time1 - time0;
> }

That will sometimes get the answer badly wrong.

The problem occurs when time_t is signed and the maximum value of time_t
is the same as the maximum value of uintmax_t. For example, a C89 system
where long is 60 bits including sign and unsigned long is 59 bits.
On such systems, the maximum possible difference is greater than the
maximum value of uintmax_t, and your subtract will get it wrong.

I gate this case by looking for time1 >=0 and time0 < 0. In fact, you can
be safer than that:

    #define HALFMAX ((uintmax_t)-1 >> 1)

    if (time1 <= HALFMAX && (time0 >= 0 || (uintmax_t) time0 >= -HALFMAX))
        return (uintmax_t) time1 - (uintmax_t) time0;

However, the remaining cases have to allow for overflow in the subtraction,
and that's the complicated bit.

Clive D.W. Feather  | Work:  <clive at demon.net>   | Tel:    +44 20 8495 6138
Internet Expert     | Home:  <clive at davros.org>  | Fax:    +44 870 051 9937
Demon Internet      | WWW: http://www.davros.org | Mobile: +44 7973 377646
Thus plc            |                            |

More information about the tz mailing list