diff options
| author | Paul Eggert | 2019-08-20 17:34:03 -0700 |
|---|---|---|
| committer | Paul Eggert | 2019-08-20 17:36:46 -0700 |
| commit | 396ed88a50fba95cd3b989965defef0130a42c42 (patch) | |
| tree | 95b03c537acf8d65b6d894a283eccf9f1d31a1e8 /src | |
| parent | 7e2090ee80c9099ee953392444e1d73d10e973d4 (diff) | |
| download | emacs-396ed88a50fba95cd3b989965defef0130a42c42.tar.gz emacs-396ed88a50fba95cd3b989965defef0130a42c42.zip | |
Avoid some excess precision in time arithmetic
* doc/misc/emacs-mime.texi (time-date):
Adjust example to match new behavior.
* etc/NEWS: Mention this.
* lisp/calendar/time-date.el (decoded-time-add)
(decoded-time--alter-second):
Don’t lose underestimate precision of seconds component.
* src/bignum.c (mpz): Grow by 1.
* src/timefns.c (trillion_factor): New function.
(timeform_sub_ps_p): Remove.
(time_arith): Avoid unnecessarily-large hz, by reducing the hz
to a value no worse than the worse hz of the two arguments.
The result is always exact unless an error is signaled.
* test/src/timefns-tests.el (timefns-tests--decode-time):
New function.
(format-time-string-with-zone): Test (decode-time LOOK ZONE t)
resolution as well as its numeric value.
Diffstat (limited to 'src')
| -rw-r--r-- | src/bignum.c | 5 | ||||
| -rw-r--r-- | src/bignum.h | 2 | ||||
| -rw-r--r-- | src/timefns.c | 106 |
3 files changed, 88 insertions, 25 deletions
diff --git a/src/bignum.c b/src/bignum.c index 3883d3a3944..90b1ebea876 100644 --- a/src/bignum.c +++ b/src/bignum.c | |||
| @@ -31,9 +31,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 31 | storage is exhausted. Admittedly this is not ideal. An mpz value | 31 | storage is exhausted. Admittedly this is not ideal. An mpz value |
| 32 | in a temporary is made permanent by mpz_swapping it with a bignum's | 32 | in a temporary is made permanent by mpz_swapping it with a bignum's |
| 33 | value. Although typically at most two temporaries are needed, | 33 | value. Although typically at most two temporaries are needed, |
| 34 | time_arith, rounddiv_q and rounding_driver each need four. */ | 34 | rounddiv_q and rounding_driver both need four and time_arith needs |
| 35 | five. */ | ||
| 35 | 36 | ||
| 36 | mpz_t mpz[4]; | 37 | mpz_t mpz[5]; |
| 37 | 38 | ||
| 38 | static void * | 39 | static void * |
| 39 | xrealloc_for_gmp (void *ptr, size_t ignore, size_t size) | 40 | xrealloc_for_gmp (void *ptr, size_t ignore, size_t size) |
diff --git a/src/bignum.h b/src/bignum.h index a9c7a0a09a8..9a32ffb0374 100644 --- a/src/bignum.h +++ b/src/bignum.h | |||
| @@ -41,7 +41,7 @@ struct Lisp_Bignum | |||
| 41 | mpz_t value; | 41 | mpz_t value; |
| 42 | } GCALIGNED_STRUCT; | 42 | } GCALIGNED_STRUCT; |
| 43 | 43 | ||
| 44 | extern mpz_t mpz[4]; | 44 | extern mpz_t mpz[5]; |
| 45 | 45 | ||
| 46 | extern void init_bignum (void); | 46 | extern void init_bignum (void); |
| 47 | extern Lisp_Object make_integer_mpz (void); | 47 | extern Lisp_Object make_integer_mpz (void); |
diff --git a/src/timefns.c b/src/timefns.c index 3b686eb2265..3c4c15b6576 100644 --- a/src/timefns.c +++ b/src/timefns.c | |||
| @@ -99,6 +99,22 @@ mpz_t ztrillion; | |||
| 99 | # endif | 99 | # endif |
| 100 | #endif | 100 | #endif |
| 101 | 101 | ||
| 102 | /* True if the nonzero Lisp integer HZ divides evenly into a trillion. */ | ||
| 103 | static bool | ||
| 104 | trillion_factor (Lisp_Object hz) | ||
| 105 | { | ||
| 106 | if (FASTER_TIMEFNS) | ||
| 107 | { | ||
| 108 | if (FIXNUMP (hz)) | ||
| 109 | return TRILLION % XFIXNUM (hz) == 0; | ||
| 110 | if (!FIXNUM_OVERFLOW_P (TRILLION)) | ||
| 111 | return false; | ||
| 112 | } | ||
| 113 | verify (TRILLION <= INTMAX_MAX); | ||
| 114 | intmax_t ihz; | ||
| 115 | return integer_to_intmax (hz, &ihz) && TRILLION % ihz == 0; | ||
| 116 | } | ||
| 117 | |||
| 102 | /* Return a struct timeval that is roughly equivalent to T. | 118 | /* Return a struct timeval that is roughly equivalent to T. |
| 103 | Use the least timeval not less than T. | 119 | Use the least timeval not less than T. |
| 104 | Return an extremal value if the result would overflow. */ | 120 | Return an extremal value if the result would overflow. */ |
| @@ -681,18 +697,10 @@ enum timeform | |||
| 681 | TIMEFORM_HI_LO_US, /* seconds plus microseconds (HI LO US) */ | 697 | TIMEFORM_HI_LO_US, /* seconds plus microseconds (HI LO US) */ |
| 682 | TIMEFORM_NIL, /* current time in nanoseconds */ | 698 | TIMEFORM_NIL, /* current time in nanoseconds */ |
| 683 | TIMEFORM_HI_LO_US_PS, /* seconds plus micro and picoseconds (HI LO US PS) */ | 699 | TIMEFORM_HI_LO_US_PS, /* seconds plus micro and picoseconds (HI LO US PS) */ |
| 684 | /* These two should be last; see timeform_sub_ps_p. */ | ||
| 685 | TIMEFORM_FLOAT, /* time as a float */ | 700 | TIMEFORM_FLOAT, /* time as a float */ |
| 686 | TIMEFORM_TICKS_HZ /* fractional time: HI is ticks, LO is ticks per second */ | 701 | TIMEFORM_TICKS_HZ /* fractional time: HI is ticks, LO is ticks per second */ |
| 687 | }; | 702 | }; |
| 688 | 703 | ||
| 689 | /* True if Lisp times of form FORM can express sub-picosecond timestamps. */ | ||
| 690 | static bool | ||
| 691 | timeform_sub_ps_p (enum timeform form) | ||
| 692 | { | ||
| 693 | return TIMEFORM_FLOAT <= form; | ||
| 694 | } | ||
| 695 | |||
| 696 | /* From the valid form FORM and the time components HIGH, LOW, USEC | 704 | /* From the valid form FORM and the time components HIGH, LOW, USEC |
| 697 | and PSEC, generate the corresponding time value. If LOW is | 705 | and PSEC, generate the corresponding time value. If LOW is |
| 698 | floating point, the other components should be zero and FORM should | 706 | floating point, the other components should be zero and FORM should |
| @@ -1080,9 +1088,14 @@ time_arith (Lisp_Object a, Lisp_Object b, bool subtract) | |||
| 1080 | else | 1088 | else |
| 1081 | { | 1089 | { |
| 1082 | /* The plan is to decompose ta into na/da and tb into nb/db. | 1090 | /* The plan is to decompose ta into na/da and tb into nb/db. |
| 1083 | Start by computing da and db. */ | 1091 | Start by computing da and db, their minimum (which will be |
| 1092 | needed later) and the iticks temporary that will become | ||
| 1093 | available once only their minimum is needed. */ | ||
| 1084 | mpz_t const *da = bignum_integer (&mpz[1], ta.hz); | 1094 | mpz_t const *da = bignum_integer (&mpz[1], ta.hz); |
| 1085 | mpz_t const *db = bignum_integer (&mpz[2], tb.hz); | 1095 | mpz_t const *db = bignum_integer (&mpz[2], tb.hz); |
| 1096 | bool da_lt_db = mpz_cmp (*da, *db) < 0; | ||
| 1097 | mpz_t const *hzmin = da_lt_db ? da : db; | ||
| 1098 | mpz_t *iticks = &mpz[da_lt_db + 1]; | ||
| 1086 | 1099 | ||
| 1087 | /* The plan is to compute (na * (db/g) + nb * (da/g)) / lcm (da, db) | 1100 | /* The plan is to compute (na * (db/g) + nb * (da/g)) / lcm (da, db) |
| 1088 | where g = gcd (da, db). Start by computing g. */ | 1101 | where g = gcd (da, db). Start by computing g. */ |
| @@ -1090,34 +1103,83 @@ time_arith (Lisp_Object a, Lisp_Object b, bool subtract) | |||
| 1090 | mpz_gcd (*g, *da, *db); | 1103 | mpz_gcd (*g, *da, *db); |
| 1091 | 1104 | ||
| 1092 | /* fa = da/g, fb = db/g. */ | 1105 | /* fa = da/g, fb = db/g. */ |
| 1093 | mpz_t *fa = &mpz[1], *fb = &mpz[3]; | 1106 | mpz_t *fa = &mpz[4], *fb = &mpz[3]; |
| 1094 | mpz_tdiv_q (*fa, *da, *g); | 1107 | mpz_tdiv_q (*fa, *da, *g); |
| 1095 | mpz_tdiv_q (*fb, *db, *g); | 1108 | mpz_tdiv_q (*fb, *db, *g); |
| 1096 | 1109 | ||
| 1097 | /* FIXME: Maybe omit need for extra temp by computing fa * db here? */ | 1110 | /* ihz = fa * db. This is equal to lcm (da, db). */ |
| 1098 | 1111 | mpz_t *ihz = &mpz[0]; | |
| 1099 | /* hz = fa * db. This is equal to lcm (da, db). */ | 1112 | mpz_mul (*ihz, *fa, *db); |
| 1100 | mpz_mul (mpz[0], *fa, *db); | 1113 | |
| 1101 | hz = make_integer_mpz (); | 1114 | /* When warning about obsolete timestamps, if the smaller |
| 1115 | denominator comes from a non-(TICKS . HZ) timestamp and could | ||
| 1116 | generate a (TICKS . HZ) timestamp that would look obsolete, | ||
| 1117 | arrange for the result to have a higher HZ to avoid a | ||
| 1118 | spurious warning by a later consumer of this function's | ||
| 1119 | returned value. */ | ||
| 1120 | verify (1 << LO_TIME_BITS <= ULONG_MAX); | ||
| 1121 | if (WARN_OBSOLETE_TIMESTAMPS | ||
| 1122 | && (da_lt_db ? aform : bform) == TIMEFORM_FLOAT | ||
| 1123 | && (da_lt_db ? bform : aform) != TIMEFORM_TICKS_HZ | ||
| 1124 | && mpz_cmp_ui (*hzmin, 1) > 0 | ||
| 1125 | && mpz_cmp_ui (*hzmin, 1 << LO_TIME_BITS) < 0) | ||
| 1126 | { | ||
| 1127 | mpz_t *hzmin1 = &mpz[2 - da_lt_db]; | ||
| 1128 | mpz_set_ui (*hzmin1, 1 << LO_TIME_BITS); | ||
| 1129 | hzmin = hzmin1; | ||
| 1130 | } | ||
| 1102 | 1131 | ||
| 1103 | /* ticks = (fb * na) OPER (fa * nb), where OPER is + or -. | 1132 | /* iticks = (fb * na) OP (fa * nb), where OP is + or -. */ |
| 1104 | OP is the multiply-add or multiply-sub form of OPER. */ | 1133 | mpz_t const *na = bignum_integer (iticks, ta.ticks); |
| 1105 | mpz_t const *na = bignum_integer (&mpz[0], ta.ticks); | 1134 | mpz_mul (*iticks, *fb, *na); |
| 1106 | mpz_mul (mpz[0], *fb, *na); | ||
| 1107 | mpz_t const *nb = bignum_integer (&mpz[3], tb.ticks); | 1135 | mpz_t const *nb = bignum_integer (&mpz[3], tb.ticks); |
| 1108 | (subtract ? mpz_submul : mpz_addmul) (mpz[0], *fa, *nb); | 1136 | (subtract ? mpz_submul : mpz_addmul) (*iticks, *fa, *nb); |
| 1137 | |||
| 1138 | /* Normalize iticks/ihz by dividing both numerator and | ||
| 1139 | denominator by ig = gcd (iticks, ihz). However, if that | ||
| 1140 | would cause the denominator to become less than hzmin, | ||
| 1141 | rescale the denominator upwards from its ordinary value by | ||
| 1142 | multiplying numerator and denominator so that the denominator | ||
| 1143 | becomes at least hzmin. This rescaling avoids returning a | ||
| 1144 | timestamp that is less precise than both a and b, or a | ||
| 1145 | timestamp that looks obsolete when that might be a problem. */ | ||
| 1146 | mpz_t *ig = &mpz[3]; | ||
| 1147 | mpz_gcd (*ig, *iticks, *ihz); | ||
| 1148 | |||
| 1149 | if (!FASTER_TIMEFNS || mpz_cmp_ui (*ig, 1) > 0) | ||
| 1150 | { | ||
| 1151 | mpz_tdiv_q (*iticks, *iticks, *ig); | ||
| 1152 | mpz_tdiv_q (*ihz, *ihz, *ig); | ||
| 1153 | |||
| 1154 | if (!FASTER_TIMEFNS || mpz_cmp (*ihz, *hzmin) < 0) | ||
| 1155 | { | ||
| 1156 | /* Rescale straightforwardly. Although this might not | ||
| 1157 | yield the minimal denominator that preserves numeric | ||
| 1158 | value and is at least hzmin, calculating such a | ||
| 1159 | denominator would be too expensive because it would | ||
| 1160 | require testing multisets of factors of lcm (da, db). */ | ||
| 1161 | mpz_t *rescale = &mpz[3]; | ||
| 1162 | mpz_cdiv_q (*rescale, *hzmin, *ihz); | ||
| 1163 | mpz_mul (*iticks, *iticks, *rescale); | ||
| 1164 | mpz_mul (*ihz, *ihz, *rescale); | ||
| 1165 | } | ||
| 1166 | } | ||
| 1167 | hz = make_integer_mpz (); | ||
| 1168 | mpz_swap (mpz[0], *iticks); | ||
| 1109 | ticks = make_integer_mpz (); | 1169 | ticks = make_integer_mpz (); |
| 1110 | } | 1170 | } |
| 1111 | 1171 | ||
| 1112 | /* Return an integer if the timestamp resolution is 1, | 1172 | /* Return an integer if the timestamp resolution is 1, |
| 1113 | otherwise the (TICKS . HZ) form if !CURRENT_TIME_LIST or if | 1173 | otherwise the (TICKS . HZ) form if !CURRENT_TIME_LIST or if |
| 1114 | either input form supports timestamps that cannot be expressed | 1174 | either input used (TICKS . HZ) form or the result can't be expressed |
| 1115 | exactly in (HI LO US PS) form, otherwise the (HI LO US PS) form | 1175 | exactly in (HI LO US PS) form, otherwise the (HI LO US PS) form |
| 1116 | for backward compatibility. */ | 1176 | for backward compatibility. */ |
| 1117 | return (EQ (hz, make_fixnum (1)) | 1177 | return (EQ (hz, make_fixnum (1)) |
| 1118 | ? ticks | 1178 | ? ticks |
| 1119 | : (!CURRENT_TIME_LIST | 1179 | : (!CURRENT_TIME_LIST |
| 1120 | || timeform_sub_ps_p (aform) || timeform_sub_ps_p (bform)) | 1180 | || aform == TIMEFORM_TICKS_HZ |
| 1181 | || bform == TIMEFORM_TICKS_HZ | ||
| 1182 | || !trillion_factor (hz)) | ||
| 1121 | ? Fcons (ticks, hz) | 1183 | ? Fcons (ticks, hz) |
| 1122 | : ticks_hz_list4 (ticks, hz)); | 1184 | : ticks_hz_list4 (ticks, hz)); |
| 1123 | } | 1185 | } |