difftime

Paul Eggert eggert at CS.UCLA.EDU
Thu Oct 21 20:33:30 UTC 2004


"Olson, Arthur David (NIH/NCI)" <olsona at dc37a.nci.nih.gov> writes:

> + #ifndef TYPE_INTEGRAL
> + #define TYPE_INTEGRAL(type) (((type) 0.4) == 0)
> + #endif /* !defined TYPE_INTEGRAL */

A couple of nits: C99 no longer calls these "integral types"; they're
just "integer types".  Also, this test is incorrect if "type" is the
C99 type "bool", since ((bool) 0.4) yields 1.  Perhaps a better test
would be:

  #define TYPE_IS_INTEGER(type) (((type) 0.5) != 0.5)

or something like that.

> #include "private.h"	/* for TYPE_INTEGRAL and TYPE_SIGNED */

The proposed code would no longer used TYPE_SIGNED, so that part
of the comment isn't needed.

> 		if (sizeof (time_t) >= sizeof (double))
> 			return time1 - time0;
> 		else	return (double) time1 - (double) time0;

You might want to add a comment here saying that we assume that
more-precise representations require more size.  The C Standard
doesn't require this, but it's a pretty-safe assumption in practice.

> 	} else {
> 		/*
> 		** time_t is integral.
> 		** As elsewhere in the time zone package,
> 		** use modular arithmetic to avoid overflow.
> 		*/
> 		register time_t	lead;
> 		register time_t	trail;
>
> 		lead = time1 / 2 - time0 / 2;

This won't work if time_t is unsigned and if time1/2 < time0/2.  In
that case "lead" should be negative, but the above code will compute a
positive value.

> 		trail = time1 % 2 - time0 % 2;

If time_t is floating, the compiler must issue a diagnostic here.
Most compilers will reject the program.

> 		return 2 * ((double) lead) + trail;

A minor point: it's a bit more elegant to write "return 2.0 * lead + trail;".

However, there is a more important problem with the last line: it
suffers from a double-rounding problem.  In general, converting "lead"
to double will lose information, and will cause a rounding error.
Multiplying by 2 is exact (on all hosts of practical interest), but
adding "trail" will cause another rounding error.

I don't see any easy way to work around this problem.

The code that I proposed (with Clive Feather's advice about padding
bits) also suffers from a double-rounding error, but it will be far
less of a practical problem, as it can occur only on very weird hosts
with padding bits where UINTMAX_MAX / 2 < INTMAX_MAX.  In contrast, I
think the double-rounding problem above can occur on ordinary hosts
with IEEE-754 floating point and 64-bit signed time_t.

There's also a efficiency problem with the current code: it uses the
integer-arithmetic approach even when it's not needed.  For example,
in the common case when time_t is a 32-bit integer and "double" is
IEEE-754 double, it's faster and simpler to convert the time_t to
double and subtract the doubles, whereas the proposed approach has
some extra integer bit-twiddling and an extra floating-point
multiplication.



More information about the tz mailing list