aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2019-08-15 10:40:11 -0700
committerPaul Eggert2019-08-15 10:41:40 -0700
commitaf82a6248ce77f1b14f89cfee677250ff024c2c4 (patch)
treef93ac7f0f37c16ad1cbf1292d3427d7357ac3e52 /src
parentf6ae51c71d69b4d1a02fc8f6536f3f8cc0dc1009 (diff)
downloademacs-af82a6248ce77f1b14f89cfee677250ff024c2c4.tar.gz
emacs-af82a6248ce77f1b14f89cfee677250ff024c2c4.zip
Fix rounding errors with float timestamps
When converting from float to (TICKS . HZ) form, do the conversion exactly. When converting from (TICKS . HZ) form to float, round to even precisely. This way, successfully converting a float to (TICKS . HZ) and back yields a value numerically equal to the original. * src/timefns.c (flt_radix_power_size): New constant. (flt_radix_power): New static var. (decode_float_time): Convert the exact numeric value rather than guessing TIMESPEC_HZ resolution. (s_ns_to_double): Remove; no longer needed. (frac_to_double): New function. (decode_ticks_hz): It is now the caller’s responsibility to pass a valid TICKS and HZ. All callers changed. Use frac_to_double to round (TICKS . HZ) precisely. (decode_time_components): When decoding nil, use decode_ticks_hz since it rounds precisely. (syms_of_timefns): Initialize flt_radix_power. * test/src/timefns-tests.el (float-time-precision): New test.
Diffstat (limited to 'src')
-rw-r--r--src/timefns.c221
1 files changed, 143 insertions, 78 deletions
diff --git a/src/timefns.c b/src/timefns.c
index 953e246a9ae..e9d1a9bf64b 100644
--- a/src/timefns.c
+++ b/src/timefns.c
@@ -368,31 +368,56 @@ lo_time (time_t t)
368 return make_fixnum (t & ((1 << LO_TIME_BITS) - 1)); 368 return make_fixnum (t & ((1 << LO_TIME_BITS) - 1));
369} 369}
370 370
371/* When converting a double to a fraction TICKS / HZ, HZ is equal to
372 FLT_RADIX * P where 0 <= P < FLT_RADIX_POWER_SIZE. The tiniest
373 nonzero double uses the maximum P. */
374enum { flt_radix_power_size = DBL_MANT_DIG - DBL_MIN_EXP + 1 };
375
376/* A integer vector of size flt_radix_power_size. The Pth entry
377 equals FLT_RADIX**P. */
378static Lisp_Object flt_radix_power;
379
371/* Convert T into an Emacs time *RESULT, truncating toward minus infinity. 380/* Convert T into an Emacs time *RESULT, truncating toward minus infinity.
372 Return zero if successful, an error number otherwise. */ 381 Return zero if successful, an error number otherwise. */
373static int 382static int
374decode_float_time (double t, struct lisp_time *result) 383decode_float_time (double t, struct lisp_time *result)
375{ 384{
376 if (!isfinite (t)) 385 Lisp_Object ticks, hz;
377 return isnan (t) ? EINVAL : EOVERFLOW; 386 if (t == 0)
378 /* Actual hz unknown; guess TIMESPEC_HZ. */ 387 {
379 mpz_set_d (mpz[1], t); 388 ticks = make_fixnum (0);
380 mpz_set_si (mpz[0], floor ((t - trunc (t)) * TIMESPEC_HZ)); 389 hz = make_fixnum (1);
381 mpz_addmul_ui (mpz[0], mpz[1], TIMESPEC_HZ); 390 }
382 result->ticks = make_integer_mpz (); 391 else
383 result->hz = timespec_hz; 392 {
393 int exponent = ilogb (t);
394 if (exponent == FP_ILOGBNAN)
395 return EINVAL;
396
397 /* An enormous or infinite T would make SCALE < 0 which would make
398 HZ < 1, which the (TICKS . HZ) representation does not allow. */
399 if (DBL_MANT_DIG - 1 < exponent)
400 return EOVERFLOW;
401
402 /* min so we don't scale tiny numbers as if they were normalized. */
403 int scale = min (DBL_MANT_DIG - 1 - exponent, flt_radix_power_size - 1);
404
405 double scaled = scalbn (t, scale);
406 eassert (trunc (scaled) == scaled);
407 ticks = double_to_integer (scaled);
408 hz = AREF (flt_radix_power, scale);
409 if (NILP (hz))
410 {
411 mpz_ui_pow_ui (mpz[0], FLT_RADIX, scale);
412 hz = make_integer_mpz ();
413 ASET (flt_radix_power, scale, hz);
414 }
415 }
416 result->ticks = ticks;
417 result->hz = hz;
384 return 0; 418 return 0;
385} 419}
386 420
387/* Compute S + NS/TIMESPEC_HZ as a double.
388 Calls to this function suffer from double-rounding;
389 work around some of the problem by using long double. */
390static double
391s_ns_to_double (long double s, long double ns)
392{
393 return s + ns / TIMESPEC_HZ;
394}
395
396/* Make a 4-element timestamp (HI LO US PS) from TICKS and HZ. 421/* Make a 4-element timestamp (HI LO US PS) from TICKS and HZ.
397 Drop any excess precision. */ 422 Drop any excess precision. */
398static Lisp_Object 423static Lisp_Object
@@ -520,69 +545,111 @@ timespec_to_lisp (struct timespec t)
520 return Fcons (timespec_ticks (t), timespec_hz); 545 return Fcons (timespec_ticks (t), timespec_hz);
521} 546}
522 547
523/* From what should be a valid timestamp (TICKS . HZ), generate the 548/* Return NUMERATOR / DENOMINATOR, rounded to the nearest double.
524 corresponding time values. 549 Arguments must be Lisp integers, and DENOMINATOR must be nonzero. */
550static double
551frac_to_double (Lisp_Object numerator, Lisp_Object denominator)
552{
553 intmax_t intmax_numerator;
554 if (FASTER_TIMEFNS && EQ (denominator, make_fixnum (1))
555 && integer_to_intmax (numerator, &intmax_numerator))
556 return intmax_numerator;
557
558 verify (FLT_RADIX == 2 || FLT_RADIX == 16);
559 enum { LOG2_FLT_RADIX = FLT_RADIX == 2 ? 1 : 4 };
560 mpz_t *n = bignum_integer (&mpz[0], numerator);
561 mpz_t *d = bignum_integer (&mpz[1], denominator);
562 ptrdiff_t nbits = mpz_sizeinbase (*n, 2);
563 ptrdiff_t dbits = mpz_sizeinbase (*d, 2);
564 eassume (0 < nbits);
565 eassume (0 < dbits);
566 ptrdiff_t ndig = (nbits + LOG2_FLT_RADIX - 1) / LOG2_FLT_RADIX;
567 ptrdiff_t ddig = (dbits + LOG2_FLT_RADIX - 1) / LOG2_FLT_RADIX;
568
569 /* Scale with SCALE when doing integer division. That is, compute
570 (N * FLT_RADIX**SCALE) / D [or, if SCALE is negative, N / (D *
571 FLT_RADIX**-SCALE)] as a bignum, convert the bignum to double,
572 then divide the double by FLT_RADIX**SCALE. */
573 ptrdiff_t scale = ddig - ndig + DBL_MANT_DIG + 1;
574 if (scale < 0)
575 {
576 mpz_mul_2exp (mpz[1], *d, - (scale * LOG2_FLT_RADIX));
577 d = &mpz[1];
578 }
579 else
580 {
581 /* min so we don't scale tiny numbers as if they were normalized. */
582 scale = min (scale, flt_radix_power_size - 1);
583
584 mpz_mul_2exp (mpz[0], *n, scale * LOG2_FLT_RADIX);
585 n = &mpz[0];
586 }
587
588 mpz_t *q = &mpz[2];
589 mpz_t *r = &mpz[3];
590 mpz_tdiv_qr (*q, *r, *n, *d);
591
592 /* The amount to add to the absolute value of *Q so that truncating
593 it to double will round correctly. */
594 int incr;
595
596 /* Round the quotient before converting it to double.
597 If the quotient is less than FLT_RADIX ** DBL_MANT_DIG,
598 round to the nearest integer; otherwise, it is less than
599 FLT_RADIX ** (DBL_MANT_DIG + 1) and round it to the nearest
600 multiple of FLT_RADIX. Break ties to even. */
601 if (mpz_sizeinbase (*q, 2) < DBL_MANT_DIG * LOG2_FLT_RADIX)
602 {
603 /* Converting to double will use the whole quotient so add 1 to
604 its absolute value as per round-to-even; i.e., if the doubled
605 remainder exceeds the denominator, or exactly equals the
606 denominator and adding 1 would make the quotient even. */
607 mpz_mul_2exp (*r, *r, 1);
608 int cmp = mpz_cmpabs (*r, *d);
609 incr = cmp > 0 || (cmp == 0 && (FASTER_TIMEFNS && FLT_RADIX == 2
610 ? mpz_odd_p (*q)
611 : mpz_tdiv_ui (*q, FLT_RADIX) & 1));
612 }
613 else
614 {
615 /* Converting to double will discard the quotient's low-order digit,
616 so add FLT_RADIX to its absolute value as per round-to-even. */
617 int lo_2digits = mpz_tdiv_ui (*q, FLT_RADIX * FLT_RADIX);
618 eassume (0 <= lo_2digits && lo_2digits < FLT_RADIX * FLT_RADIX);
619 int lo_digit = lo_2digits % FLT_RADIX;
620 incr = ((lo_digit > FLT_RADIX / 2
621 || (lo_digit == FLT_RADIX / 2 && FLT_RADIX % 2 == 0
622 && ((lo_2digits / FLT_RADIX) & 1
623 || mpz_sgn (*r) != 0)))
624 ? FLT_RADIX : 0);
625 }
626
627 /* Increment the absolute value of the quotient by INCR. */
628 if (!FASTER_TIMEFNS || incr != 0)
629 (mpz_sgn (*n) < 0 ? mpz_sub_ui : mpz_add_ui) (*q, *q, incr);
630
631 return scalbn (mpz_get_d (*q), -scale);
632}
633
634/* From a valid timestamp (TICKS . HZ), generate the corresponding
635 time values.
525 636
526 If RESULT is not null, store into *RESULT the converted time. 637 If RESULT is not null, store into *RESULT the converted time.
527 Otherwise, store into *DRESULT the number of seconds since the 638 Otherwise, store into *DRESULT the number of seconds since the
528 start of the POSIX Epoch. Unsuccessful calls may or may not store 639 start of the POSIX Epoch.
529 results.
530 640
531 Return zero if successful, an error number if (TICKS . HZ) would not 641 Return zero, which indicates success. */
532 be a valid new-format timestamp. */
533static int 642static int
534decode_ticks_hz (Lisp_Object ticks, Lisp_Object hz, 643decode_ticks_hz (Lisp_Object ticks, Lisp_Object hz,
535 struct lisp_time *result, double *dresult) 644 struct lisp_time *result, double *dresult)
536{ 645{
537 int ns;
538 mpz_t *q = &mpz[0];
539
540 if (! (INTEGERP (ticks)
541 && ((FIXNUMP (hz) && 0 < XFIXNUM (hz))
542 || (BIGNUMP (hz) && 0 < mpz_sgn (XBIGNUM (hz)->value)))))
543 return EINVAL;
544
545 if (result) 646 if (result)
546 { 647 {
547 result->ticks = ticks; 648 result->ticks = ticks;
548 result->hz = hz; 649 result->hz = hz;
549 } 650 }
550 else 651 else
551 { 652 *dresult = frac_to_double (ticks, hz);
552 if (FASTER_TIMEFNS && EQ (hz, timespec_hz))
553 {
554 if (FIXNUMP (ticks))
555 {
556 verify (1 < TIMESPEC_HZ);
557 EMACS_INT s = XFIXNUM (ticks) / TIMESPEC_HZ;
558 ns = XFIXNUM (ticks) % TIMESPEC_HZ;
559 if (ns < 0)
560 s--, ns += TIMESPEC_HZ;
561 *dresult = s_ns_to_double (s, ns);
562 return 0;
563 }
564 ns = mpz_fdiv_q_ui (*q, XBIGNUM (ticks)->value, TIMESPEC_HZ);
565 }
566 else if (FASTER_TIMEFNS && EQ (hz, make_fixnum (1)))
567 {
568 ns = 0;
569 if (FIXNUMP (ticks))
570 {
571 *dresult = XFIXNUM (ticks);
572 return 0;
573 }
574 q = &XBIGNUM (ticks)->value;
575 }
576 else
577 {
578 mpz_mul_ui (*q, *bignum_integer (&mpz[1], ticks), TIMESPEC_HZ);
579 mpz_fdiv_q (*q, *q, *bignum_integer (&mpz[1], hz));
580 ns = mpz_fdiv_q_ui (*q, *q, TIMESPEC_HZ);
581 }
582
583 *dresult = s_ns_to_double (mpz_get_d (*q), ns);
584 }
585
586 return 0; 653 return 0;
587} 654}
588 655
@@ -621,7 +688,10 @@ decode_time_components (enum timeform form,
621 return EINVAL; 688 return EINVAL;
622 689
623 case TIMEFORM_TICKS_HZ: 690 case TIMEFORM_TICKS_HZ:
624 return decode_ticks_hz (high, low, result, dresult); 691 if (INTEGERP (high)
692 && (!NILP (Fnatnump (low)) && !EQ (low, make_fixnum (0))))
693 return decode_ticks_hz (high, low, result, dresult);
694 return EINVAL;
625 695
626 case TIMEFORM_FLOAT: 696 case TIMEFORM_FLOAT:
627 { 697 {
@@ -636,17 +706,8 @@ decode_time_components (enum timeform form,
636 } 706 }
637 707
638 case TIMEFORM_NIL: 708 case TIMEFORM_NIL:
639 { 709 return decode_ticks_hz (timespec_ticks (current_timespec ()),
640 struct timespec now = current_timespec (); 710 timespec_hz, result, dresult);
641 if (result)
642 {
643 result->ticks = timespec_ticks (now);
644 result->hz = timespec_hz;
645 }
646 else
647 *dresult = s_ns_to_double (now.tv_sec, now.tv_nsec);
648 return 0;
649 }
650 711
651 default: 712 default:
652 break; 713 break;
@@ -1814,6 +1875,10 @@ syms_of_timefns (void)
1814 defsubr (&Scurrent_time_string); 1875 defsubr (&Scurrent_time_string);
1815 defsubr (&Scurrent_time_zone); 1876 defsubr (&Scurrent_time_zone);
1816 defsubr (&Sset_time_zone_rule); 1877 defsubr (&Sset_time_zone_rule);
1878
1879 flt_radix_power = make_vector (flt_radix_power_size, Qnil);
1880 staticpro (&flt_radix_power);
1881
1817#ifdef NEED_ZTRILLION_INIT 1882#ifdef NEED_ZTRILLION_INIT
1818 pdumper_do_now_and_after_load (syms_of_timefns_for_pdumper); 1883 pdumper_do_now_and_after_load (syms_of_timefns_for_pdumper);
1819#endif 1884#endif