aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2017-03-01 12:29:37 -0800
committerPaul Eggert2017-03-01 12:47:28 -0800
commit207ee94b1d1f3cbe5ddd87a4cdfae17e5ad8419d (patch)
tree6a4d82cb85a667b9cbd38d3467e2907b18dbe938 /src
parentebb105054a421faff17ee11f0cbcbed87661dd11 (diff)
downloademacs-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.c9
-rw-r--r--src/floatfns.c67
-rw-r--r--src/lisp.h8
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
379static EMACS_INT 374static 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
426static double 424static double
427double_identity (double d) 425emacs_trunc (double d)
428{ 426{
429 return d; 427 return (d < 0 ? ceil : floor) (d);
430} 428}
429#endif
431 430
432DEFUN ("ceiling", Fceiling, Sceiling, 1, 2, 0, 431DEFUN ("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.
466With optional DIVISOR, truncate ARG/DIVISOR. */) 465With 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))