aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2019-08-16 16:25:02 -0700
committerPaul Eggert2019-08-16 16:27:27 -0700
commitf9fd12a30b3d94eb48f7b309907d136d7b2682ac (patch)
treed00aa93beec6eb1013127ee251342f39f135a994 /src
parente82923c817159c751aa9c902093a46b9457e8499 (diff)
downloademacs-f9fd12a30b3d94eb48f7b309907d136d7b2682ac.tar.gz
emacs-f9fd12a30b3d94eb48f7b309907d136d7b2682ac.zip
Fix time-add rounding bug
Without this fix, time arithmetic yielded results that were not mathematically accurate, even though the exact results were representable; for example, (time-add 0 1e-13) yielded a timestamp equal to 0 instead of to 1e-13. * lisp/timezone.el (timezone-time-from-absolute): Let time-add do its thing rather than using floating point internally, which has rounding errors. We now have bignums and so don’t need floating point to avoid overflow issues. * src/timefns.c (timeform_sub_ps_p): New function. (time_arith): If either argument is a float, represent the result exactly instead of discarding sub-ps info. * test/lisp/timezone-tests.el (timezone-tests-time-from-absolute): Don’t assume (HI LO US PS) timestamp format. * test/src/emacs-module-tests.el (mod-test-add-nanosecond/valid): Don’t assume that time-add discards sub-ns info. * test/src/timefns-tests.el (time-rounding-tests): Add regression test to detect time-add rounding bug.
Diffstat (limited to 'src')
-rw-r--r--src/timefns.c14
1 files changed, 11 insertions, 3 deletions
diff --git a/src/timefns.c b/src/timefns.c
index e9d1a9bf64b..a4c1c4cb284 100644
--- a/src/timefns.c
+++ b/src/timefns.c
@@ -661,10 +661,18 @@ enum timeform
661 TIMEFORM_HI_LO_US, /* seconds plus microseconds (HI LO US) */ 661 TIMEFORM_HI_LO_US, /* seconds plus microseconds (HI LO US) */
662 TIMEFORM_NIL, /* current time in nanoseconds */ 662 TIMEFORM_NIL, /* current time in nanoseconds */
663 TIMEFORM_HI_LO_US_PS, /* seconds plus micro and picoseconds (HI LO US PS) */ 663 TIMEFORM_HI_LO_US_PS, /* seconds plus micro and picoseconds (HI LO US PS) */
664 /* These two should be last; see timeform_sub_ps_p. */
664 TIMEFORM_FLOAT, /* time as a float */ 665 TIMEFORM_FLOAT, /* time as a float */
665 TIMEFORM_TICKS_HZ /* fractional time: HI is ticks, LO is ticks per second */ 666 TIMEFORM_TICKS_HZ /* fractional time: HI is ticks, LO is ticks per second */
666 }; 667 };
667 668
669/* True if Lisp times of form FORM can express sub-picosecond timestamps. */
670static bool
671timeform_sub_ps_p (enum timeform form)
672{
673 return TIMEFORM_FLOAT <= form;
674}
675
668/* From the valid form FORM and the time components HIGH, LOW, USEC 676/* From the valid form FORM and the time components HIGH, LOW, USEC
669 and PSEC, generate the corresponding time value. If LOW is 677 and PSEC, generate the corresponding time value. If LOW is
670 floating point, the other components should be zero and FORM should 678 floating point, the other components should be zero and FORM should
@@ -1016,8 +1024,8 @@ lispint_arith (Lisp_Object a, Lisp_Object b, bool subtract)
1016 1024
1017/* Given Lisp operands A and B, add their values, and return the 1025/* Given Lisp operands A and B, add their values, and return the
1018 result as a Lisp timestamp that is in (TICKS . HZ) form if either A 1026 result as a Lisp timestamp that is in (TICKS . HZ) form if either A
1019 or B are in that form, (HI LO US PS) form otherwise. Subtract 1027 or B are in that form or are floats, (HI LO US PS) form otherwise.
1020 instead of adding if SUBTRACT. */ 1028 Subtract instead of adding if SUBTRACT. */
1021static Lisp_Object 1029static Lisp_Object
1022time_arith (Lisp_Object a, Lisp_Object b, bool subtract) 1030time_arith (Lisp_Object a, Lisp_Object b, bool subtract)
1023{ 1031{
@@ -1077,7 +1085,7 @@ time_arith (Lisp_Object a, Lisp_Object b, bool subtract)
1077 otherwise the (HI LO US PS) form for backward compatibility. */ 1085 otherwise the (HI LO US PS) form for backward compatibility. */
1078 return (EQ (hz, make_fixnum (1)) 1086 return (EQ (hz, make_fixnum (1))
1079 ? ticks 1087 ? ticks
1080 : aform == TIMEFORM_TICKS_HZ || bform == TIMEFORM_TICKS_HZ 1088 : timeform_sub_ps_p (aform) || timeform_sub_ps_p (bform)
1081 ? Fcons (ticks, hz) 1089 ? Fcons (ticks, hz)
1082 : ticks_hz_list4 (ticks, hz)); 1090 : ticks_hz_list4 (ticks, hz));
1083} 1091}