]>
Commit | Line | Data |
---|---|---|
5b2abdfb A |
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). | |
5b2abdfb A |
4 | */ |
5 | ||
9385eb3d | 6 | #include <sys/cdefs.h> |
5b2abdfb A |
7 | #ifndef lint |
8 | #ifndef NOID | |
3d9156a7 | 9 | static char elsieid[] __unused = "@(#)difftime.c 7.9"; |
5b2abdfb A |
10 | #endif /* !defined NOID */ |
11 | #endif /* !defined lint */ | |
3d9156a7 | 12 | __FBSDID("$FreeBSD: src/lib/libc/stdtime/difftime.c,v 1.8 2004/06/14 10:31:52 stefanf Exp $"); |
5b2abdfb A |
13 | |
14 | /*LINTLIBRARY*/ | |
15 | ||
9385eb3d | 16 | #include "namespace.h" |
5b2abdfb | 17 | #include "private.h" |
9385eb3d | 18 | #include "un-namespace.h" |
5b2abdfb A |
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 | ||
3d9156a7 A |
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 | } | |
5b2abdfb A |
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 | } |