]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - lib/libc/stdtime/difftime.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / lib / libc / stdtime / difftime.c
1 /*
2 ** This file is in the public domain, so clarified as of
3 ** June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov).
4 */
5
6 #include <sys/cdefs.h>
7 #ifndef lint
8 #ifndef NOID
9 static char     elsieid[] __unused = "@(#)difftime.c    7.9";
10 #endif /* !defined NOID */
11 #endif /* !defined lint */
12 __FBSDID("$FreeBSD$");
13
14 /*LINTLIBRARY*/
15
16 #include "namespace.h"
17 #include "private.h"
18 #include "un-namespace.h"
19
20 /*
21 ** Algorithm courtesy Paul Eggert (eggert@twinsun.com).
22 */
23
24 #ifdef HAVE_LONG_DOUBLE
25 #define long_double     long double
26 #endif /* defined HAVE_LONG_DOUBLE */
27 #ifndef HAVE_LONG_DOUBLE
28 #define long_double     double
29 #endif /* !defined HAVE_LONG_DOUBLE */
30
31 double
32 difftime(time1, time0)
33 const time_t    time1;
34 const time_t    time0;
35 {
36         time_t  delta;
37         time_t  hibit;
38
39         {
40                 time_t          tt;
41                 double          d;
42                 long_double     ld;
43
44                 if (sizeof tt < sizeof d)
45                         return (double) time1 - (double) time0;
46                 if (sizeof tt < sizeof ld)
47                         return (long_double) time1 - (long_double) time0;
48         }
49         if (time1 < time0)
50                 return -difftime(time0, time1);
51         /*
52         ** As much as possible, avoid loss of precision
53         ** by computing the difference before converting to double.
54         */
55         delta = time1 - time0;
56         if (delta >= 0)
57                 return delta;
58         /*
59         ** Repair delta overflow.
60         */
61         hibit = (~ (time_t) 0) << (TYPE_BIT(time_t) - 1);
62         /*
63         ** The following expression rounds twice, which means
64         ** the result may not be the closest to the true answer.
65         ** For example, suppose time_t is 64-bit signed int,
66         ** long_double is IEEE 754 double with default rounding,
67         ** time1 = 9223372036854775807 and time0 = -1536.
68         ** Then the true difference is 9223372036854777343,
69         ** which rounds to 9223372036854777856
70         ** with a total error of 513.
71         ** But delta overflows to -9223372036854774273,
72         ** which rounds to -9223372036854774784, and correcting
73         ** this by subtracting 2 * (long_double) hibit
74         ** (i.e. by adding 2**64 = 18446744073709551616)
75         ** yields 9223372036854776832, which
76         ** rounds to 9223372036854775808
77         ** with a total error of 1535 instead.
78         ** This problem occurs only with very large differences.
79         ** It's too painful to fix this portably.
80         ** We are not alone in this problem;
81         ** some C compilers round twice when converting
82         ** large unsigned types to small floating types,
83         ** so if time_t is unsigned the "return delta" above
84         ** has the same double-rounding problem with those compilers.
85         */
86         return delta - 2 * (long_double) hibit;
87 }