xref: /openbsd-src/lib/libc/time/difftime.c (revision e9b2b68ca3a6cce2632b4a73cc801bbb6f19c001)
1 #if defined(LIBC_SCCS) && !defined(lint)
2 static char rcsid[] = "$OpenBSD: difftime.c,v 1.3 1996/08/19 08:34:48 tholo Exp $";
3 #endif /* LIBC_SCCS and not lint */
4 
5 /*LINTLIBRARY*/
6 
7 #include "private.h"
8 
9 /*
10 ** Algorithm courtesy Paul Eggert (eggert@twinsun.com).
11 */
12 
13 #ifdef HAVE_LONG_DOUBLE
14 #define long_double	long double
15 #endif /* defined HAVE_LONG_DOUBLE */
16 #ifndef HAVE_LONG_DOUBLE
17 #define long_double	double
18 #endif /* !defined HAVE_LONG_DOUBLE */
19 
20 double
21 difftime(time1, time0)
22 const time_t	time1;
23 const time_t	time0;
24 {
25 	time_t	delta;
26 	time_t	hibit;
27 
28 	if (sizeof(time_t) < sizeof(double))
29 		return (double) time1 - (double) time0;
30 	if (sizeof(time_t) < sizeof(long_double))
31 		return (long_double) time1 - (long_double) time0;
32 	if (time1 < time0)
33 		return -difftime(time0, time1);
34 	/*
35 	** As much as possible, avoid loss of precision
36 	** by computing the difference before converting to double.
37 	*/
38 	delta = time1 - time0;
39 	if (delta >= 0)
40 		return delta;
41 	/*
42 	** Repair delta overflow.
43 	*/
44 	hibit = (~ (time_t) 0) << (TYPE_BIT(time_t) - 1);
45 	/*
46 	** The following expression rounds twice, which means
47 	** the result may not be the closest to the true answer.
48 	** For example, suppose time_t is 64-bit signed int,
49 	** long_double is IEEE 754 double with default rounding,
50 	** time1 = 9223372036854775807 and time0 = -1536.
51 	** Then the true difference is 9223372036854777343,
52 	** which rounds to 9223372036854777856
53 	** with a total error of 513.
54 	** But delta overflows to -9223372036854774273,
55 	** which rounds to -9223372036854774784, and correcting
56 	** this by subtracting 2 * (long_double) hibit
57 	** (i.e. by adding 2**64 = 18446744073709551616)
58 	** yields 9223372036854776832, which
59 	** rounds to 9223372036854775808
60 	** with a total error of 1535 instead.
61 	** This problem occurs only with very large differences.
62 	** It's too painful to fix this portably.
63 	** We are not alone in this problem;
64 	** some C compilers round twice when converting
65 	** large unsigned types to small floating types,
66 	** so if time_t is unsigned the "return delta" above
67 	** has the same double-rounding problem with those compilers.
68 	*/
69 	return delta - 2 * (long_double) hibit;
70 }
71