]> git.eshelyaron.com Git - emacs.git/commitdiff
Fix bugs when rounding to bignums
authorPaul Eggert <eggert@cs.ucla.edu>
Thu, 23 Aug 2018 02:30:24 +0000 (19:30 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Thu, 23 Aug 2018 02:30:57 +0000 (19:30 -0700)
Also, since Emacs historically reported a range error when
rounding operations overflowed, do that consistently for all
bignum overflows.
* doc/lispref/errors.texi (Standard Errors):
* doc/lispref/numbers.texi (Integer Basics): Document range errors.
* src/alloc.c (range_error): Rename from integer_overflow.
All uses changed.
* src/floatfns.c (rounding_driver): When the result of a floating
point rounding operation does not fit into a fixnum, put it
into a bignum instead of always signaling an range error.
* test/src/floatfns-tests.el (divide-extreme-sign):
These tests now return the mathematically-correct answer
instead of signaling an error.
(bignum-round): Check that integers round to themselves.

doc/lispref/errors.texi
doc/lispref/numbers.texi
src/alloc.c
src/data.c
src/floatfns.c
src/lisp.h
test/src/floatfns-tests.el

index a0e32c5631c3ab205f0d027d2cffb0d9b08a8fba..e61ea98e210069f6f352fc0612f68a5c4ab9d42c 100644 (file)
@@ -159,6 +159,11 @@ The message is @samp{No catch for tag}.  @xref{Catch and Throw}.
 The message is @samp{Attempt to modify a protected file}.
 @end ignore
 
+@item range-error
+The message is @code{Arithmetic range error}.
+This can happen with integers exceeding the @code{integer-width} limit.
+@xref{Integer Basics}.
+
 @item scan-error
 The message is @samp{Scan error}.  This happens when certain
 syntax-parsing functions find invalid syntax or mismatched
@@ -223,9 +228,6 @@ The message is @samp{Arithmetic domain error}.
 The message is @samp{Arithmetic overflow error}.  This is a subcategory
 of @code{domain-error}.
 
-@item range-error
-The message is @code{Arithmetic range error}.
-
 @item singularity-error
 The message is @samp{Arithmetic singularity error}.  This is a
 subcategory of @code{domain-error}.
index a8150478613b7b035192234058b6165d90911d98..d03113674f54bff097f2eb8f3403fb91b77f8c24 100644 (file)
@@ -201,7 +201,7 @@ range are limited to absolute values less than
 @math{2^{n}},
 @end tex
 where @var{n} is this variable's value.  Attempts to create bignums outside
-this range result in an integer overflow error.  Setting this variable
+this range signal a range error.  Setting this variable
 to zero disables creation of bignums; setting it to a large number can
 cause Emacs to consume large quantities of memory if a computation
 creates huge integers.
index 24a24aab96bd31c5796f909aabaa53066e0db3a8..cdcd465ac5a23e364869519091937ed33292e738 100644 (file)
@@ -3771,7 +3771,7 @@ make_number (mpz_t value)
   /* The documentation says integer-width should be nonnegative, so
      a single comparison suffices even though 'bits' is unsigned.  */
   if (integer_width < bits)
-    integer_overflow ();
+    range_error ();
 
   struct Lisp_Bignum *b = ALLOCATE_PSEUDOVECTOR (struct Lisp_Bignum, value,
                                                 PVEC_BIGNUM);
@@ -7203,9 +7203,9 @@ verify_alloca (void)
 /* Memory allocation for GMP.  */
 
 void
-integer_overflow (void)
+range_error (void)
 {
-  error ("Integer too large to be represented");
+  xsignal0 (Qrange_error);
 }
 
 static void *
index 08c7271dd798b73f091d0bb084348b61d68515ab..170a74a6589d68e949485a6569cfecb919eaca22 100644 (file)
@@ -2406,7 +2406,7 @@ static void
 emacs_mpz_mul (mpz_t rop, mpz_t const op1, mpz_t const op2)
 {
   if (NLIMBS_LIMIT - emacs_mpz_size (op1) < emacs_mpz_size (op2))
-    integer_overflow ();
+    range_error ();
   mpz_mul (rop, op1, op2);
 }
 
@@ -2420,7 +2420,7 @@ emacs_mpz_mul_2exp (mpz_t rop, mpz_t const op1, mp_bitcnt_t op2)
 
   mp_bitcnt_t op2limbs = op2 / GMP_NUMB_BITS;
   if (lim - emacs_mpz_size (op1) < op2limbs)
-    integer_overflow ();
+    range_error ();
   mpz_mul_2exp (rop, op1, op2);
 }
 
@@ -2434,7 +2434,7 @@ emacs_mpz_pow_ui (mpz_t rop, mpz_t const base, unsigned long exp)
 
   int nbase = emacs_mpz_size (base), n;
   if (INT_MULTIPLY_WRAPV (nbase, exp, &n) || lim < n)
-    integer_overflow ();
+    range_error ();
   mpz_pow_ui (rop, base, exp);
 }
 
@@ -3398,7 +3398,7 @@ expt_integer (Lisp_Object x, Lisp_Object y)
           && mpz_fits_ulong_p (XBIGNUM (y)->value))
     exp = mpz_get_ui (XBIGNUM (y)->value);
   else
-    integer_overflow ();
+    range_error ();
 
   mpz_t val;
   mpz_init (val);
index c09fe9d6a5b408f8c4ffc8169b8cad2852a720b4..e7884864eef291285325f1f19233717a57bd9729 100644 (file)
@@ -410,7 +410,12 @@ rounding_driver (Lisp_Object arg, Lisp_Object divisor,
       if (! FIXNUM_OVERFLOW_P (ir))
        return make_fixnum (ir);
     }
-  xsignal2 (Qrange_error, build_string (name), arg);
+  mpz_t drz;
+  mpz_init (drz);
+  mpz_set_d (drz, dr);
+  Lisp_Object rounded = make_number (drz);
+  mpz_clear (drz);
+  return rounded;
 }
 
 static void
@@ -501,13 +506,20 @@ systems, but 2 on others.  */)
   return rounding_driver (arg, divisor, emacs_rint, rounddiv_q, "round");
 }
 
+/* Since rounding_driver truncates anyway, no need to call 'trunc'.  */
+static double
+identity (double x)
+{
+  return x;
+}
+
 DEFUN ("truncate", Ftruncate, Struncate, 1, 2, 0,
        doc: /* Truncate a floating point number to an int.
 Rounds ARG toward zero.
 With optional DIVISOR, truncate ARG/DIVISOR.  */)
   (Lisp_Object arg, Lisp_Object divisor)
 {
-  return rounding_driver (arg, divisor, trunc, mpz_tdiv_q, "truncate");
+  return rounding_driver (arg, divisor, identity, mpz_tdiv_q, "truncate");
 }
 
 
index c5593b210088be1fc1b3afc737f8fa1423517387..bca4dfbb603a762f7ad7002bc6b1d8e05fdecd19 100644 (file)
@@ -3708,7 +3708,7 @@ extern void display_malloc_warning (void);
 extern ptrdiff_t inhibit_garbage_collection (void);
 extern Lisp_Object build_overlay (Lisp_Object, Lisp_Object, Lisp_Object);
 extern void free_cons (struct Lisp_Cons *);
-extern _Noreturn void integer_overflow (void);
+extern _Noreturn void range_error (void);
 extern void init_alloc_once (void);
 extern void init_alloc (void);
 extern void syms_of_alloc (void);
index 592efce359d8bdacaae6e9e09e60a6f20bb4e50f..d41b08f7965c0e92b20934149ab41db14f41936e 100644 (file)
 (require 'ert)
 
 (ert-deftest divide-extreme-sign ()
-  (should-error (ceiling most-negative-fixnum -1.0))
-  (should-error (floor most-negative-fixnum -1.0))
-  (should-error (round most-negative-fixnum -1.0))
-  (should-error (truncate most-negative-fixnum -1.0)))
+  (should (= (ceiling most-negative-fixnum -1.0) (- most-negative-fixnum)))
+  (should (= (floor most-negative-fixnum -1.0) (- most-negative-fixnum)))
+  (should (= (round most-negative-fixnum -1.0) (- most-negative-fixnum)))
+  (should (= (truncate most-negative-fixnum -1.0) (- most-negative-fixnum))))
 
 (ert-deftest logb-extreme-fixnum ()
   (should (= (logb most-negative-fixnum) (1+ (logb most-positive-fixnum)))))
                   (1+ most-positive-fixnum)
                   (* most-positive-fixnum most-positive-fixnum))))
     (dolist (n ns)
+      (should (= n (ceiling n)))
+      (should (= n (floor n)))
+      (should (= n (round n)))
+      (should (= n (truncate n)))
       (dolist (d ns)
         (let ((q (/ n d))
               (r (% n d))