diff options
| author | Paul Eggert | 2019-11-13 13:07:01 -0800 |
|---|---|---|
| committer | Paul Eggert | 2019-11-13 13:10:09 -0800 |
| commit | bede5984246ba734c93fc28148b5f8e1b14d30c5 (patch) | |
| tree | d5ec0482e1ed6b8dad42e65c9d9ea4eb2e2b75ff /src/floatfns.c | |
| parent | 02e637ecca3b1419d2a6c433eca72c5728c65051 (diff) | |
| download | emacs-bede5984246ba734c93fc28148b5f8e1b14d30c5.tar.gz emacs-bede5984246ba734c93fc28148b5f8e1b14d30c5.zip | |
Fix double-rounding bug in ceiling etc.
This is doable now that we have bignums.
* src/floatfns.c (integer_value): Remove; no longer used.
(rescale_for_division): New function.
(rounding_driver): Use it to divide properly (by using bignums)
even when arguments are float, fixing a double-rounding FIXME.
* src/lisp.h (LOG2_FLT_RADIX): Move here ...
* src/timefns.c (frac_to_double): ... from here.
* test/src/floatfns-tests.el (big-round):
Add a test to catch the double-rounding bug.
Diffstat (limited to 'src/floatfns.c')
| -rw-r--r-- | src/floatfns.c | 115 |
1 files changed, 50 insertions, 65 deletions
diff --git a/src/floatfns.c b/src/floatfns.c index 7e77dbd16dc..a626845377a 100644 --- a/src/floatfns.c +++ b/src/floatfns.c | |||
| @@ -335,19 +335,6 @@ This is the same as the exponent of a float. */) | |||
| 335 | return make_fixnum (value); | 335 | return make_fixnum (value); |
| 336 | } | 336 | } |
| 337 | 337 | ||
| 338 | /* True if A is exactly representable as an integer. */ | ||
| 339 | |||
| 340 | static bool | ||
| 341 | integer_value (Lisp_Object a) | ||
| 342 | { | ||
| 343 | if (FLOATP (a)) | ||
| 344 | { | ||
| 345 | double d = XFLOAT_DATA (a); | ||
| 346 | return d == floor (d) && isfinite (d); | ||
| 347 | } | ||
| 348 | return true; | ||
| 349 | } | ||
| 350 | |||
| 351 | /* Return the integer exponent E such that D * FLT_RADIX**E (i.e., | 338 | /* Return the integer exponent E such that D * FLT_RADIX**E (i.e., |
| 352 | scalbn (D, E)) is an integer that has precision equal to D and is | 339 | scalbn (D, E)) is an integer that has precision equal to D and is |
| 353 | representable as a double. | 340 | representable as a double. |
| @@ -371,70 +358,68 @@ double_integer_scale (double d) | |||
| 371 | && (FP_ILOGB0 != INT_MIN || d != 0))))); | 358 | && (FP_ILOGB0 != INT_MIN || d != 0))))); |
| 372 | } | 359 | } |
| 373 | 360 | ||
| 361 | /* Convert the Lisp number N to an integer and return a pointer to the | ||
| 362 | converted integer, represented as an mpz_t *. Use *T as a | ||
| 363 | temporary; the returned value might be T. Scale N by the maximum | ||
| 364 | of NSCALE and DSCALE while converting. If NSCALE is nonzero, N | ||
| 365 | must be a float; signal an overflow if NSCALE is greater than | ||
| 366 | DBL_MANT_DIG - DBL_MIN_EXP, otherwise scalbn (XFLOAT_DATA (N), NSCALE) | ||
| 367 | must return an integer value, without rounding or overflow. */ | ||
| 368 | |||
| 369 | static mpz_t const * | ||
| 370 | rescale_for_division (Lisp_Object n, mpz_t *t, int nscale, int dscale) | ||
| 371 | { | ||
| 372 | mpz_t const *pn; | ||
| 373 | |||
| 374 | if (FLOATP (n)) | ||
| 375 | { | ||
| 376 | if (DBL_MANT_DIG - DBL_MIN_EXP < nscale) | ||
| 377 | overflow_error (); | ||
| 378 | mpz_set_d (*t, scalbn (XFLOAT_DATA (n), nscale)); | ||
| 379 | pn = t; | ||
| 380 | } | ||
| 381 | else | ||
| 382 | pn = bignum_integer (t, n); | ||
| 383 | |||
| 384 | if (nscale < dscale) | ||
| 385 | { | ||
| 386 | emacs_mpz_mul_2exp (*t, *pn, (dscale - nscale) * LOG2_FLT_RADIX); | ||
| 387 | pn = t; | ||
| 388 | } | ||
| 389 | return pn; | ||
| 390 | } | ||
| 391 | |||
| 374 | /* the rounding functions */ | 392 | /* the rounding functions */ |
| 375 | 393 | ||
| 376 | static Lisp_Object | 394 | static Lisp_Object |
| 377 | rounding_driver (Lisp_Object arg, Lisp_Object divisor, | 395 | rounding_driver (Lisp_Object n, Lisp_Object d, |
| 378 | double (*double_round) (double), | 396 | double (*double_round) (double), |
| 379 | void (*int_divide) (mpz_t, mpz_t const, mpz_t const), | 397 | void (*int_divide) (mpz_t, mpz_t const, mpz_t const), |
| 380 | EMACS_INT (*fixnum_divide) (EMACS_INT, EMACS_INT)) | 398 | EMACS_INT (*fixnum_divide) (EMACS_INT, EMACS_INT)) |
| 381 | { | 399 | { |
| 382 | CHECK_NUMBER (arg); | 400 | CHECK_NUMBER (n); |
| 383 | 401 | ||
| 384 | double d; | 402 | if (NILP (d)) |
| 385 | if (NILP (divisor)) | 403 | return FLOATP (n) ? double_to_integer (double_round (XFLOAT_DATA (n))) : n; |
| 386 | { | ||
| 387 | if (! FLOATP (arg)) | ||
| 388 | return arg; | ||
| 389 | d = XFLOAT_DATA (arg); | ||
| 390 | } | ||
| 391 | else | ||
| 392 | { | ||
| 393 | CHECK_NUMBER (divisor); | ||
| 394 | if (integer_value (arg) && integer_value (divisor)) | ||
| 395 | { | ||
| 396 | /* Divide as integers. Converting to double might lose | ||
| 397 | info, even for fixnums; also see the FIXME below. */ | ||
| 398 | |||
| 399 | if (FLOATP (arg)) | ||
| 400 | arg = double_to_integer (XFLOAT_DATA (arg)); | ||
| 401 | if (FLOATP (divisor)) | ||
| 402 | divisor = double_to_integer (XFLOAT_DATA (divisor)); | ||
| 403 | |||
| 404 | if (FIXNUMP (divisor)) | ||
| 405 | { | ||
| 406 | if (XFIXNUM (divisor) == 0) | ||
| 407 | xsignal0 (Qarith_error); | ||
| 408 | if (FIXNUMP (arg)) | ||
| 409 | return make_int (fixnum_divide (XFIXNUM (arg), | ||
| 410 | XFIXNUM (divisor))); | ||
| 411 | } | ||
| 412 | int_divide (mpz[0], | ||
| 413 | *bignum_integer (&mpz[0], arg), | ||
| 414 | *bignum_integer (&mpz[1], divisor)); | ||
| 415 | return make_integer_mpz (); | ||
| 416 | } | ||
| 417 | 404 | ||
| 418 | double f1 = XFLOATINT (arg); | 405 | CHECK_NUMBER (d); |
| 419 | double f2 = XFLOATINT (divisor); | ||
| 420 | if (! IEEE_FLOATING_POINT && f2 == 0) | ||
| 421 | xsignal0 (Qarith_error); | ||
| 422 | /* FIXME: This division rounds, so the result is double-rounded. */ | ||
| 423 | d = f1 / f2; | ||
| 424 | } | ||
| 425 | 406 | ||
| 426 | /* Round, coarsely test for fixnum overflow before converting to | 407 | if (FIXNUMP (d)) |
| 427 | EMACS_INT (to avoid undefined C behavior), and then exactly test | ||
| 428 | for overflow after converting (as FIXNUM_OVERFLOW_P is inaccurate | ||
| 429 | on floats). */ | ||
| 430 | double dr = double_round (d); | ||
| 431 | if (fabs (dr) < 2 * (MOST_POSITIVE_FIXNUM + 1)) | ||
| 432 | { | 408 | { |
| 433 | EMACS_INT ir = dr; | 409 | if (XFIXNUM (d) == 0) |
| 434 | if (! FIXNUM_OVERFLOW_P (ir)) | 410 | xsignal0 (Qarith_error); |
| 435 | return make_fixnum (ir); | 411 | |
| 412 | /* Divide fixnum by fixnum specially, for speed. */ | ||
| 413 | if (FIXNUMP (n)) | ||
| 414 | return make_int (fixnum_divide (XFIXNUM (n), XFIXNUM (d))); | ||
| 436 | } | 415 | } |
| 437 | return double_to_integer (dr); | 416 | |
| 417 | int nscale = FLOATP (n) ? double_integer_scale (XFLOAT_DATA (n)) : 0; | ||
| 418 | int dscale = FLOATP (d) ? double_integer_scale (XFLOAT_DATA (d)) : 0; | ||
| 419 | int_divide (mpz[0], | ||
| 420 | *rescale_for_division (n, &mpz[0], nscale, dscale), | ||
| 421 | *rescale_for_division (d, &mpz[1], dscale, nscale)); | ||
| 422 | return make_integer_mpz (); | ||
| 438 | } | 423 | } |
| 439 | 424 | ||
| 440 | static EMACS_INT | 425 | static EMACS_INT |