diff options
| author | Paul Eggert | 2017-03-01 12:29:37 -0800 |
|---|---|---|
| committer | Paul Eggert | 2017-03-01 12:47:28 -0800 |
| commit | 207ee94b1d1f3cbe5ddd87a4cdfae17e5ad8419d (patch) | |
| tree | 6a4d82cb85a667b9cbd38d3467e2907b18dbe938 /src | |
| parent | ebb105054a421faff17ee11f0cbcbed87661dd11 (diff) | |
| download | emacs-207ee94b1d1f3cbe5ddd87a4cdfae17e5ad8419d.tar.gz emacs-207ee94b1d1f3cbe5ddd87a4cdfae17e5ad8419d.zip | |
Fix rounding error in ‘ceiling’ etc.
Without this fix, (ceiling most-negative-fixnum -1.0) returns
most-negative-fixnum instead of correctly signaling range-error,
and similarly for floor, round, and truncate.
* configure.ac (trunc): Add a check, since Gnulib’s doc says
‘trunc’ is missing from MSVC 9. The Gnulib doc says ‘trunc’ is
also missing from some other older operating systems like Solaris
9 which I know we don’t care about any more, so MSVC is the only
reason to worry about ‘trunc’ here.
* src/editfns.c (styled_format): Formatting a float with %c is now an
error. The old code did not work in general, because FIXNUM_OVERFLOW_P
had rounding errors. Besides, the "if (FLOATP (...))" was in there
only as a result of my misunderstanding old code that I introduced
2011. Although %d etc. is sometimes used on floats that represent
huge UIDs or PIDs etc. that do not fit in fixnums, this cannot
happen with characters.
* src/floatfns.c (rounding_driver): Rework to do the right thing
when the intermediate result equals 2.305843009213694e+18, i.e.,
is exactly 1 greater than MOST_POSITIVE_FIXNUM on a 64-bit host.
Simplify so that only one section of code checks for overflow,
rather than two.
(double_identity): Remove. All uses changed to ...
(emacs_trunc): ... this new function. Add replacement for
platforms that lack ‘trunc’.
* src/lisp.h (FIXNUM_OVERFLOW_P, make_fixnum_or_float):
Make it clear that the arg cannot be floating point.
* test/src/editfns-tests.el (format-c-float): New test.
* test/src/floatfns-tests.el: New file, to test for this bug.
Diffstat (limited to 'src')
| -rw-r--r-- | src/editfns.c | 9 | ||||
| -rw-r--r-- | src/floatfns.c | 67 | ||||
| -rw-r--r-- | src/lisp.h | 8 |
3 files changed, 38 insertions, 46 deletions
diff --git a/src/editfns.c b/src/editfns.c index 4618164d008..e3c8548b5a4 100644 --- a/src/editfns.c +++ b/src/editfns.c | |||
| @@ -4119,12 +4119,6 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) | |||
| 4119 | } | 4119 | } |
| 4120 | else if (conversion == 'c') | 4120 | else if (conversion == 'c') |
| 4121 | { | 4121 | { |
| 4122 | if (FLOATP (args[n])) | ||
| 4123 | { | ||
| 4124 | double d = XFLOAT_DATA (args[n]); | ||
| 4125 | args[n] = make_number (FIXNUM_OVERFLOW_P (d) ? -1 : d); | ||
| 4126 | } | ||
| 4127 | |||
| 4128 | if (INTEGERP (args[n]) && ! ASCII_CHAR_P (XINT (args[n]))) | 4122 | if (INTEGERP (args[n]) && ! ASCII_CHAR_P (XINT (args[n]))) |
| 4129 | { | 4123 | { |
| 4130 | if (!multibyte) | 4124 | if (!multibyte) |
| @@ -4241,7 +4235,8 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) | |||
| 4241 | || conversion == 'X')) | 4235 | || conversion == 'X')) |
| 4242 | error ("Invalid format operation %%%c", | 4236 | error ("Invalid format operation %%%c", |
| 4243 | STRING_CHAR ((unsigned char *) format - 1)); | 4237 | STRING_CHAR ((unsigned char *) format - 1)); |
| 4244 | else if (! NUMBERP (args[n])) | 4238 | else if (! (INTEGERP (args[n]) |
| 4239 | || (FLOATP (args[n]) && conversion != 'c'))) | ||
| 4245 | error ("Format specifier doesn't match argument type"); | 4240 | error ("Format specifier doesn't match argument type"); |
| 4246 | else | 4241 | else |
| 4247 | { | 4242 | { |
diff --git a/src/floatfns.c b/src/floatfns.c index c476627b33b..96711faff62 100644 --- a/src/floatfns.c +++ b/src/floatfns.c | |||
| @@ -36,7 +36,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 36 | isnormal, isunordered, lgamma, log1p, *log2 [via (log X 2)], *logb | 36 | isnormal, isunordered, lgamma, log1p, *log2 [via (log X 2)], *logb |
| 37 | (approximately), lrint/llrint, lround/llround, nan, nearbyint, | 37 | (approximately), lrint/llrint, lround/llround, nan, nearbyint, |
| 38 | nextafter, nexttoward, remainder, remquo, *rint, round, scalbln, | 38 | nextafter, nexttoward, remainder, remquo, *rint, round, scalbln, |
| 39 | scalbn, signbit, tgamma, trunc. | 39 | scalbn, signbit, tgamma, *trunc. |
| 40 | */ | 40 | */ |
| 41 | 41 | ||
| 42 | #include <config.h> | 42 | #include <config.h> |
| @@ -333,47 +333,42 @@ rounding_driver (Lisp_Object arg, Lisp_Object divisor, | |||
| 333 | { | 333 | { |
| 334 | CHECK_NUMBER_OR_FLOAT (arg); | 334 | CHECK_NUMBER_OR_FLOAT (arg); |
| 335 | 335 | ||
| 336 | if (! NILP (divisor)) | 336 | double d; |
| 337 | if (NILP (divisor)) | ||
| 338 | { | ||
| 339 | if (! FLOATP (arg)) | ||
| 340 | return arg; | ||
| 341 | d = XFLOAT_DATA (arg); | ||
| 342 | } | ||
| 343 | else | ||
| 337 | { | 344 | { |
| 338 | EMACS_INT i1, i2; | ||
| 339 | |||
| 340 | CHECK_NUMBER_OR_FLOAT (divisor); | 345 | CHECK_NUMBER_OR_FLOAT (divisor); |
| 341 | 346 | if (!FLOATP (arg) && !FLOATP (divisor)) | |
| 342 | if (FLOATP (arg) || FLOATP (divisor)) | ||
| 343 | { | 347 | { |
| 344 | double f1, f2; | 348 | if (XINT (divisor) == 0) |
| 345 | |||
| 346 | f1 = FLOATP (arg) ? XFLOAT_DATA (arg) : XINT (arg); | ||
| 347 | f2 = (FLOATP (divisor) ? XFLOAT_DATA (divisor) : XINT (divisor)); | ||
| 348 | if (! IEEE_FLOATING_POINT && f2 == 0) | ||
| 349 | xsignal0 (Qarith_error); | 349 | xsignal0 (Qarith_error); |
| 350 | 350 | return make_number (int_round2 (XINT (arg), XINT (divisor))); | |
| 351 | f1 = (*double_round) (f1 / f2); | ||
| 352 | if (FIXNUM_OVERFLOW_P (f1)) | ||
| 353 | xsignal3 (Qrange_error, build_string (name), arg, divisor); | ||
| 354 | arg = make_number (f1); | ||
| 355 | return arg; | ||
| 356 | } | 351 | } |
| 357 | 352 | ||
| 358 | i1 = XINT (arg); | 353 | double f1 = FLOATP (arg) ? XFLOAT_DATA (arg) : XINT (arg); |
| 359 | i2 = XINT (divisor); | 354 | double f2 = FLOATP (divisor) ? XFLOAT_DATA (divisor) : XINT (divisor); |
| 360 | 355 | if (! IEEE_FLOATING_POINT && f2 == 0) | |
| 361 | if (i2 == 0) | ||
| 362 | xsignal0 (Qarith_error); | 356 | xsignal0 (Qarith_error); |
| 363 | 357 | d = f1 / f2; | |
| 364 | XSETINT (arg, (*int_round2) (i1, i2)); | ||
| 365 | return arg; | ||
| 366 | } | 358 | } |
| 367 | 359 | ||
| 368 | if (FLOATP (arg)) | 360 | /* Round, coarsely test for fixnum overflow before converting to |
| 361 | EMACS_INT (to avoid undefined C behavior), and then exactly test | ||
| 362 | for overflow after converting (as FIXNUM_OVERFLOW_P is inaccurate | ||
| 363 | on floats). */ | ||
| 364 | double dr = double_round (d); | ||
| 365 | if (fabs (dr) < 2 * (MOST_POSITIVE_FIXNUM + 1)) | ||
| 369 | { | 366 | { |
| 370 | double d = (*double_round) (XFLOAT_DATA (arg)); | 367 | EMACS_INT ir = dr; |
| 371 | if (FIXNUM_OVERFLOW_P (d)) | 368 | if (! FIXNUM_OVERFLOW_P (ir)) |
| 372 | xsignal2 (Qrange_error, build_string (name), arg); | 369 | return make_number (ir); |
| 373 | arg = make_number (d); | ||
| 374 | } | 370 | } |
| 375 | 371 | xsignal2 (Qrange_error, build_string (name), arg); | |
| 376 | return arg; | ||
| 377 | } | 372 | } |
| 378 | 373 | ||
| 379 | static EMACS_INT | 374 | static EMACS_INT |
| @@ -423,11 +418,15 @@ emacs_rint (double d) | |||
| 423 | } | 418 | } |
| 424 | #endif | 419 | #endif |
| 425 | 420 | ||
| 421 | #ifdef HAVE_TRUNC | ||
| 422 | #define emacs_trunc trunc | ||
| 423 | #else | ||
| 426 | static double | 424 | static double |
| 427 | double_identity (double d) | 425 | emacs_trunc (double d) |
| 428 | { | 426 | { |
| 429 | return d; | 427 | return (d < 0 ? ceil : floor) (d); |
| 430 | } | 428 | } |
| 429 | #endif | ||
| 431 | 430 | ||
| 432 | DEFUN ("ceiling", Fceiling, Sceiling, 1, 2, 0, | 431 | DEFUN ("ceiling", Fceiling, Sceiling, 1, 2, 0, |
| 433 | doc: /* Return the smallest integer no less than ARG. | 432 | doc: /* Return the smallest integer no less than ARG. |
| @@ -466,7 +465,7 @@ Rounds ARG toward zero. | |||
| 466 | With optional DIVISOR, truncate ARG/DIVISOR. */) | 465 | With optional DIVISOR, truncate ARG/DIVISOR. */) |
| 467 | (Lisp_Object arg, Lisp_Object divisor) | 466 | (Lisp_Object arg, Lisp_Object divisor) |
| 468 | { | 467 | { |
| 469 | return rounding_driver (arg, divisor, double_identity, truncate2, | 468 | return rounding_driver (arg, divisor, emacs_trunc, truncate2, |
| 470 | "truncate"); | 469 | "truncate"); |
| 471 | } | 470 | } |
| 472 | 471 | ||
diff --git a/src/lisp.h b/src/lisp.h index 238c20bc189..a757dfdbb31 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -1031,9 +1031,7 @@ INLINE bool | |||
| 1031 | return lisp_h_EQ (x, y); | 1031 | return lisp_h_EQ (x, y); |
| 1032 | } | 1032 | } |
| 1033 | 1033 | ||
| 1034 | /* Value is true if I doesn't fit into a Lisp fixnum. It is | 1034 | /* True if the possibly-unsigned integer I doesn't fit in a Lisp fixnum. */ |
| 1035 | written this way so that it also works if I is of unsigned | ||
| 1036 | type or if I is a NaN. */ | ||
| 1037 | 1035 | ||
| 1038 | #define FIXNUM_OVERFLOW_P(i) \ | 1036 | #define FIXNUM_OVERFLOW_P(i) \ |
| 1039 | (! ((0 <= (i) || MOST_NEGATIVE_FIXNUM <= (i)) && (i) <= MOST_POSITIVE_FIXNUM)) | 1037 | (! ((0 <= (i) || MOST_NEGATIVE_FIXNUM <= (i)) && (i) <= MOST_POSITIVE_FIXNUM)) |
| @@ -4374,8 +4372,8 @@ extern void init_system_name (void); | |||
| 4374 | because 'abs' is reserved by the C standard. */ | 4372 | because 'abs' is reserved by the C standard. */ |
| 4375 | #define eabs(x) ((x) < 0 ? -(x) : (x)) | 4373 | #define eabs(x) ((x) < 0 ? -(x) : (x)) |
| 4376 | 4374 | ||
| 4377 | /* Return a fixnum or float, depending on whether VAL fits in a Lisp | 4375 | /* Return a fixnum or float, depending on whether the integer VAL fits |
| 4378 | fixnum. */ | 4376 | in a Lisp fixnum. */ |
| 4379 | 4377 | ||
| 4380 | #define make_fixnum_or_float(val) \ | 4378 | #define make_fixnum_or_float(val) \ |
| 4381 | (FIXNUM_OVERFLOW_P (val) ? make_float (val) : make_number (val)) | 4379 | (FIXNUM_OVERFLOW_P (val) ? make_float (val) : make_number (val)) |