aboutsummaryrefslogtreecommitdiffstats
path: root/src/floatfns.c
diff options
context:
space:
mode:
authorPaul Eggert2019-11-13 13:07:01 -0800
committerPaul Eggert2019-11-13 13:10:09 -0800
commitbede5984246ba734c93fc28148b5f8e1b14d30c5 (patch)
treed5ec0482e1ed6b8dad42e65c9d9ea4eb2e2b75ff /src/floatfns.c
parent02e637ecca3b1419d2a6c433eca72c5728c65051 (diff)
downloademacs-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.c115
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
340static bool
341integer_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
369static mpz_t const *
370rescale_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
376static Lisp_Object 394static Lisp_Object
377rounding_driver (Lisp_Object arg, Lisp_Object divisor, 395rounding_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
440static EMACS_INT 425static EMACS_INT