# Difftime code

Clive D.W. Feather clive at demon.net
Fri Aug 6 07:35:53 UTC 2004

```Being in a hurry this morning, I wrote:
>>       || (TYPE_FLOATING (time_t) && sizeof (time_t) != sizeof (long double)))
> Okay, this proves that time_t isn't long double. Clever.

That is, if this is true then time_t isn't long double. Still clever.

> 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.

On C99, this can only happen if UINTMAX_MAX == INTMAX_MAX. On C89 it can
only happen if ULONG_MAX == LONG_MAX. If you're willing to ensure these
symbols are present on non-Standard systems, you can filter out many cases
at compile time.

> 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.

The worst case on such a system (using ** for powers) is:

time1       == 2**59 - 1
time0       == -2**59
UINTMAX_MAX == 2**59 - 1
time1-time0 == 2**60 - 1, which will overflow and come out as 2**59 - 1

> 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;

There are in fact many ways to filter out easy cases:
* If time1 and time0 have the same sign (treating 0 as positive) then you
can't have an overflow (original posting).
* If time1 and -time0 both have the top bit zero, you can't have an
overflow (posting quoted just above).
* If time1/2 - time0/2 is less than UINTMAX_MAX/2, you can't have an
overflow.

Attempting to summarize yet again:

* If time_t is a floating point type, we can just do the calculation in
long double. Furthermore, unless it's long double we can do it in double.
[Note, however, that C99 left open the possibility of more floating
point types in future standards.]
* If time_t is an integer type, we can nearly always convert to uintmax_t,
subtract, and convert to double.
* There are a few implementations where this subtraction could overflow.
There are compile- and run-time tests which can identify whether a
particular call to difftime is "at risk"; if it is, it then becomes
necessary to either:
- determine whether the simple subtraction is correct or is too low by
UINTMAX_MAX + 1, and adjust the result in the latter case;
- do the calculation in floating point, risking a loss of precision.

Any code which fails to do all of this is sometimes going to get the wrong