aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggert2018-08-22 19:30:24 -0700
committerPaul Eggert2018-08-22 19:30:57 -0700
commitee641b87cf220250ba89f219fb47a4406a05deb7 (patch)
tree08ff44c5197ae39b2ec0906de4bb4dcafda4677f
parentbe5fe6183e95f3afe3a62ec43504b99df90bc794 (diff)
downloademacs-ee641b87cf220250ba89f219fb47a4406a05deb7.tar.gz
emacs-ee641b87cf220250ba89f219fb47a4406a05deb7.zip
Fix bugs when rounding to bignums
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.
-rw-r--r--doc/lispref/errors.texi8
-rw-r--r--doc/lispref/numbers.texi2
-rw-r--r--src/alloc.c6
-rw-r--r--src/data.c8
-rw-r--r--src/floatfns.c16
-rw-r--r--src/lisp.h2
-rw-r--r--test/src/floatfns-tests.el12
7 files changed, 36 insertions, 18 deletions
diff --git a/doc/lispref/errors.texi b/doc/lispref/errors.texi
index a0e32c5631c..e61ea98e210 100644
--- a/doc/lispref/errors.texi
+++ b/doc/lispref/errors.texi
@@ -159,6 +159,11 @@ The message is @samp{No catch for tag}. @xref{Catch and Throw}.
159The message is @samp{Attempt to modify a protected file}. 159The message is @samp{Attempt to modify a protected file}.
160@end ignore 160@end ignore
161 161
162@item range-error
163The message is @code{Arithmetic range error}.
164This can happen with integers exceeding the @code{integer-width} limit.
165@xref{Integer Basics}.
166
162@item scan-error 167@item scan-error
163The message is @samp{Scan error}. This happens when certain 168The message is @samp{Scan error}. This happens when certain
164syntax-parsing functions find invalid syntax or mismatched 169syntax-parsing functions find invalid syntax or mismatched
@@ -223,9 +228,6 @@ The message is @samp{Arithmetic domain error}.
223The message is @samp{Arithmetic overflow error}. This is a subcategory 228The message is @samp{Arithmetic overflow error}. This is a subcategory
224of @code{domain-error}. 229of @code{domain-error}.
225 230
226@item range-error
227The message is @code{Arithmetic range error}.
228
229@item singularity-error 231@item singularity-error
230The message is @samp{Arithmetic singularity error}. This is a 232The message is @samp{Arithmetic singularity error}. This is a
231subcategory of @code{domain-error}. 233subcategory of @code{domain-error}.
diff --git a/doc/lispref/numbers.texi b/doc/lispref/numbers.texi
index a8150478613..d03113674f5 100644
--- a/doc/lispref/numbers.texi
+++ b/doc/lispref/numbers.texi
@@ -201,7 +201,7 @@ range are limited to absolute values less than
201@math{2^{n}}, 201@math{2^{n}},
202@end tex 202@end tex
203where @var{n} is this variable's value. Attempts to create bignums outside 203where @var{n} is this variable's value. Attempts to create bignums outside
204this range result in an integer overflow error. Setting this variable 204this range signal a range error. Setting this variable
205to zero disables creation of bignums; setting it to a large number can 205to zero disables creation of bignums; setting it to a large number can
206cause Emacs to consume large quantities of memory if a computation 206cause Emacs to consume large quantities of memory if a computation
207creates huge integers. 207creates huge integers.
diff --git a/src/alloc.c b/src/alloc.c
index 24a24aab96b..cdcd465ac5a 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -3771,7 +3771,7 @@ make_number (mpz_t value)
3771 /* The documentation says integer-width should be nonnegative, so 3771 /* The documentation says integer-width should be nonnegative, so
3772 a single comparison suffices even though 'bits' is unsigned. */ 3772 a single comparison suffices even though 'bits' is unsigned. */
3773 if (integer_width < bits) 3773 if (integer_width < bits)
3774 integer_overflow (); 3774 range_error ();
3775 3775
3776 struct Lisp_Bignum *b = ALLOCATE_PSEUDOVECTOR (struct Lisp_Bignum, value, 3776 struct Lisp_Bignum *b = ALLOCATE_PSEUDOVECTOR (struct Lisp_Bignum, value,
3777 PVEC_BIGNUM); 3777 PVEC_BIGNUM);
@@ -7203,9 +7203,9 @@ verify_alloca (void)
7203/* Memory allocation for GMP. */ 7203/* Memory allocation for GMP. */
7204 7204
7205void 7205void
7206integer_overflow (void) 7206range_error (void)
7207{ 7207{
7208 error ("Integer too large to be represented"); 7208 xsignal0 (Qrange_error);
7209} 7209}
7210 7210
7211static void * 7211static void *
diff --git a/src/data.c b/src/data.c
index 08c7271dd79..170a74a6589 100644
--- a/src/data.c
+++ b/src/data.c
@@ -2406,7 +2406,7 @@ static void
2406emacs_mpz_mul (mpz_t rop, mpz_t const op1, mpz_t const op2) 2406emacs_mpz_mul (mpz_t rop, mpz_t const op1, mpz_t const op2)
2407{ 2407{
2408 if (NLIMBS_LIMIT - emacs_mpz_size (op1) < emacs_mpz_size (op2)) 2408 if (NLIMBS_LIMIT - emacs_mpz_size (op1) < emacs_mpz_size (op2))
2409 integer_overflow (); 2409 range_error ();
2410 mpz_mul (rop, op1, op2); 2410 mpz_mul (rop, op1, op2);
2411} 2411}
2412 2412
@@ -2420,7 +2420,7 @@ emacs_mpz_mul_2exp (mpz_t rop, mpz_t const op1, mp_bitcnt_t op2)
2420 2420
2421 mp_bitcnt_t op2limbs = op2 / GMP_NUMB_BITS; 2421 mp_bitcnt_t op2limbs = op2 / GMP_NUMB_BITS;
2422 if (lim - emacs_mpz_size (op1) < op2limbs) 2422 if (lim - emacs_mpz_size (op1) < op2limbs)
2423 integer_overflow (); 2423 range_error ();
2424 mpz_mul_2exp (rop, op1, op2); 2424 mpz_mul_2exp (rop, op1, op2);
2425} 2425}
2426 2426
@@ -2434,7 +2434,7 @@ emacs_mpz_pow_ui (mpz_t rop, mpz_t const base, unsigned long exp)
2434 2434
2435 int nbase = emacs_mpz_size (base), n; 2435 int nbase = emacs_mpz_size (base), n;
2436 if (INT_MULTIPLY_WRAPV (nbase, exp, &n) || lim < n) 2436 if (INT_MULTIPLY_WRAPV (nbase, exp, &n) || lim < n)
2437 integer_overflow (); 2437 range_error ();
2438 mpz_pow_ui (rop, base, exp); 2438 mpz_pow_ui (rop, base, exp);
2439} 2439}
2440 2440
@@ -3398,7 +3398,7 @@ expt_integer (Lisp_Object x, Lisp_Object y)
3398 && mpz_fits_ulong_p (XBIGNUM (y)->value)) 3398 && mpz_fits_ulong_p (XBIGNUM (y)->value))
3399 exp = mpz_get_ui (XBIGNUM (y)->value); 3399 exp = mpz_get_ui (XBIGNUM (y)->value);
3400 else 3400 else
3401 integer_overflow (); 3401 range_error ();
3402 3402
3403 mpz_t val; 3403 mpz_t val;
3404 mpz_init (val); 3404 mpz_init (val);
diff --git a/src/floatfns.c b/src/floatfns.c
index c09fe9d6a5b..e7884864eef 100644
--- a/src/floatfns.c
+++ b/src/floatfns.c
@@ -410,7 +410,12 @@ rounding_driver (Lisp_Object arg, Lisp_Object divisor,
410 if (! FIXNUM_OVERFLOW_P (ir)) 410 if (! FIXNUM_OVERFLOW_P (ir))
411 return make_fixnum (ir); 411 return make_fixnum (ir);
412 } 412 }
413 xsignal2 (Qrange_error, build_string (name), arg); 413 mpz_t drz;
414 mpz_init (drz);
415 mpz_set_d (drz, dr);
416 Lisp_Object rounded = make_number (drz);
417 mpz_clear (drz);
418 return rounded;
414} 419}
415 420
416static void 421static void
@@ -501,13 +506,20 @@ systems, but 2 on others. */)
501 return rounding_driver (arg, divisor, emacs_rint, rounddiv_q, "round"); 506 return rounding_driver (arg, divisor, emacs_rint, rounddiv_q, "round");
502} 507}
503 508
509/* Since rounding_driver truncates anyway, no need to call 'trunc'. */
510static double
511identity (double x)
512{
513 return x;
514}
515
504DEFUN ("truncate", Ftruncate, Struncate, 1, 2, 0, 516DEFUN ("truncate", Ftruncate, Struncate, 1, 2, 0,
505 doc: /* Truncate a floating point number to an int. 517 doc: /* Truncate a floating point number to an int.
506Rounds ARG toward zero. 518Rounds ARG toward zero.
507With optional DIVISOR, truncate ARG/DIVISOR. */) 519With optional DIVISOR, truncate ARG/DIVISOR. */)
508 (Lisp_Object arg, Lisp_Object divisor) 520 (Lisp_Object arg, Lisp_Object divisor)
509{ 521{
510 return rounding_driver (arg, divisor, trunc, mpz_tdiv_q, "truncate"); 522 return rounding_driver (arg, divisor, identity, mpz_tdiv_q, "truncate");
511} 523}
512 524
513 525
diff --git a/src/lisp.h b/src/lisp.h
index c5593b21008..bca4dfbb603 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -3708,7 +3708,7 @@ extern void display_malloc_warning (void);
3708extern ptrdiff_t inhibit_garbage_collection (void); 3708extern ptrdiff_t inhibit_garbage_collection (void);
3709extern Lisp_Object build_overlay (Lisp_Object, Lisp_Object, Lisp_Object); 3709extern Lisp_Object build_overlay (Lisp_Object, Lisp_Object, Lisp_Object);
3710extern void free_cons (struct Lisp_Cons *); 3710extern void free_cons (struct Lisp_Cons *);
3711extern _Noreturn void integer_overflow (void); 3711extern _Noreturn void range_error (void);
3712extern void init_alloc_once (void); 3712extern void init_alloc_once (void);
3713extern void init_alloc (void); 3713extern void init_alloc (void);
3714extern void syms_of_alloc (void); 3714extern void syms_of_alloc (void);
diff --git a/test/src/floatfns-tests.el b/test/src/floatfns-tests.el
index 592efce359d..d41b08f7965 100644
--- a/test/src/floatfns-tests.el
+++ b/test/src/floatfns-tests.el
@@ -20,10 +20,10 @@
20(require 'ert) 20(require 'ert)
21 21
22(ert-deftest divide-extreme-sign () 22(ert-deftest divide-extreme-sign ()
23 (should-error (ceiling most-negative-fixnum -1.0)) 23 (should (= (ceiling most-negative-fixnum -1.0) (- most-negative-fixnum)))
24 (should-error (floor most-negative-fixnum -1.0)) 24 (should (= (floor most-negative-fixnum -1.0) (- most-negative-fixnum)))
25 (should-error (round most-negative-fixnum -1.0)) 25 (should (= (round most-negative-fixnum -1.0) (- most-negative-fixnum)))
26 (should-error (truncate most-negative-fixnum -1.0))) 26 (should (= (truncate most-negative-fixnum -1.0) (- most-negative-fixnum))))
27 27
28(ert-deftest logb-extreme-fixnum () 28(ert-deftest logb-extreme-fixnum ()
29 (should (= (logb most-negative-fixnum) (1+ (logb most-positive-fixnum))))) 29 (should (= (logb most-negative-fixnum) (1+ (logb most-positive-fixnum)))))
@@ -66,6 +66,10 @@
66 (1+ most-positive-fixnum) 66 (1+ most-positive-fixnum)
67 (* most-positive-fixnum most-positive-fixnum)))) 67 (* most-positive-fixnum most-positive-fixnum))))
68 (dolist (n ns) 68 (dolist (n ns)
69 (should (= n (ceiling n)))
70 (should (= n (floor n)))
71 (should (= n (round n)))
72 (should (= n (truncate n)))
69 (dolist (d ns) 73 (dolist (d ns)
70 (let ((q (/ n d)) 74 (let ((q (/ n d))
71 (r (% n d)) 75 (r (% n d))