From c44bc4d370b38ac3e9da15579fd372d1410d4b4c Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Thu, 13 Sep 2018 14:28:56 -0700 Subject: [PATCH] Fix (floor 54043195528445955 3.0) bug * src/floatfns.c (rounding_driver): Fix rounding error that can occur when both args have values exactly representable as integers but at least one arg is a float. * test/src/floatfns-tests.el (big-round): New test. --- src/floatfns.c | 20 +++++++++++++++++++- test/src/floatfns-tests.el | 4 ++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/floatfns.c b/src/floatfns.c index 6f5aee2db9d..900392575c0 100644 --- a/src/floatfns.c +++ b/src/floatfns.c @@ -332,6 +332,18 @@ This is the same as the exponent of a float. */) return make_fixnum (value); } +/* True if A is exactly representable as an integer. */ + +static bool +integer_value (Lisp_Object a) +{ + if (FLOATP (a)) + { + double d = XFLOAT_DATA (a); + return d == floor (d) && isfinite (d); + } + return true; +} /* the rounding functions */ @@ -353,10 +365,16 @@ rounding_driver (Lisp_Object arg, Lisp_Object divisor, else { CHECK_NUMBER (divisor); - if (!FLOATP (arg) && !FLOATP (divisor)) + if (integer_value (arg) && integer_value (divisor)) { /* Divide as integers. Converting to double might lose info, even for fixnums; also see the FIXME below. */ + + if (FLOATP (arg)) + arg = double_to_integer (XFLOAT_DATA (arg)); + if (FLOATP (divisor)) + divisor = double_to_integer (XFLOAT_DATA (divisor)); + if (FIXNUMP (divisor)) { if (XFIXNUM (divisor) == 0) diff --git a/test/src/floatfns-tests.el b/test/src/floatfns-tests.el index 3dcddc7f26b..14576b603c0 100644 --- a/test/src/floatfns-tests.el +++ b/test/src/floatfns-tests.el @@ -109,4 +109,8 @@ (should-error (round n d)) (should-error (truncate n d))))))) +(ert-deftest big-round () + (should (= (floor 54043195528445955 3) + (floor 54043195528445955 3.0)))) + (provide 'floatfns-tests) -- 2.39.2