]>
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 | |
9385eb3d | 9 | static char elsieid[] __unused = "@(#)difftime.c 7.7"; |
5b2abdfb A |
10 | #endif /* !defined NOID */ |
11 | #endif /* !defined lint */ | |
9385eb3d | 12 | __FBSDID("$FreeBSD: src/lib/libc/stdtime/difftime.c,v 1.7 2003/02/16 17:29:11 nectar 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 | ||
39 | if (sizeof(time_t) < sizeof(double)) | |
40 | return (double) time1 - (double) time0; | |
41 | if (sizeof(time_t) < sizeof(long_double)) | |
42 | return (long_double) time1 - (long_double) time0; | |
43 | if (time1 < time0) | |
44 | return -difftime(time0, time1); | |
45 | /* | |
46 | ** As much as possible, avoid loss of precision | |
47 | ** by computing the difference before converting to double. | |
48 | */ | |
49 | delta = time1 - time0; | |
50 | if (delta >= 0) | |
51 | return delta; | |
52 | /* | |
53 | ** Repair delta overflow. | |
54 | */ | |
55 | hibit = (~ (time_t) 0) << (TYPE_BIT(time_t) - 1); | |
56 | /* | |
57 | ** The following expression rounds twice, which means | |
58 | ** the result may not be the closest to the true answer. | |
59 | ** For example, suppose time_t is 64-bit signed int, | |
60 | ** long_double is IEEE 754 double with default rounding, | |
61 | ** time1 = 9223372036854775807 and time0 = -1536. | |
62 | ** Then the true difference is 9223372036854777343, | |
63 | ** which rounds to 9223372036854777856 | |
64 | ** with a total error of 513. | |
65 | ** But delta overflows to -9223372036854774273, | |
66 | ** which rounds to -9223372036854774784, and correcting | |
67 | ** this by subtracting 2 * (long_double) hibit | |
68 | ** (i.e. by adding 2**64 = 18446744073709551616) | |
69 | ** yields 9223372036854776832, which | |
70 | ** rounds to 9223372036854775808 | |
71 | ** with a total error of 1535 instead. | |
72 | ** This problem occurs only with very large differences. | |
73 | ** It's too painful to fix this portably. | |
74 | ** We are not alone in this problem; | |
75 | ** some C compilers round twice when converting | |
76 | ** large unsigned types to small floating types, | |
77 | ** so if time_t is unsigned the "return delta" above | |
78 | ** has the same double-rounding problem with those compilers. | |
79 | */ | |
80 | return delta - 2 * (long_double) hibit; | |
81 | } |