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