diff options
| author | Paul Eggert | 2018-08-22 19:30:24 -0700 |
|---|---|---|
| committer | Paul Eggert | 2018-08-22 19:30:57 -0700 |
| commit | ee641b87cf220250ba89f219fb47a4406a05deb7 (patch) | |
| tree | 08ff44c5197ae39b2ec0906de4bb4dcafda4677f /src | |
| parent | be5fe6183e95f3afe3a62ec43504b99df90bc794 (diff) | |
| download | emacs-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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/alloc.c | 6 | ||||
| -rw-r--r-- | src/data.c | 8 | ||||
| -rw-r--r-- | src/floatfns.c | 16 | ||||
| -rw-r--r-- | src/lisp.h | 2 |
4 files changed, 22 insertions, 10 deletions
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 | ||
| 7205 | void | 7205 | void |
| 7206 | integer_overflow (void) | 7206 | range_error (void) |
| 7207 | { | 7207 | { |
| 7208 | error ("Integer too large to be represented"); | 7208 | xsignal0 (Qrange_error); |
| 7209 | } | 7209 | } |
| 7210 | 7210 | ||
| 7211 | static void * | 7211 | static 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 | |||
| 2406 | emacs_mpz_mul (mpz_t rop, mpz_t const op1, mpz_t const op2) | 2406 | emacs_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 | ||
| 416 | static void | 421 | static 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'. */ | ||
| 510 | static double | ||
| 511 | identity (double x) | ||
| 512 | { | ||
| 513 | return x; | ||
| 514 | } | ||
| 515 | |||
| 504 | DEFUN ("truncate", Ftruncate, Struncate, 1, 2, 0, | 516 | DEFUN ("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. |
| 506 | Rounds ARG toward zero. | 518 | Rounds ARG toward zero. |
| 507 | With optional DIVISOR, truncate ARG/DIVISOR. */) | 519 | With 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); | |||
| 3708 | extern ptrdiff_t inhibit_garbage_collection (void); | 3708 | extern ptrdiff_t inhibit_garbage_collection (void); |
| 3709 | extern Lisp_Object build_overlay (Lisp_Object, Lisp_Object, Lisp_Object); | 3709 | extern Lisp_Object build_overlay (Lisp_Object, Lisp_Object, Lisp_Object); |
| 3710 | extern void free_cons (struct Lisp_Cons *); | 3710 | extern void free_cons (struct Lisp_Cons *); |
| 3711 | extern _Noreturn void integer_overflow (void); | 3711 | extern _Noreturn void range_error (void); |
| 3712 | extern void init_alloc_once (void); | 3712 | extern void init_alloc_once (void); |
| 3713 | extern void init_alloc (void); | 3713 | extern void init_alloc (void); |
| 3714 | extern void syms_of_alloc (void); | 3714 | extern void syms_of_alloc (void); |