aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggert2018-09-22 08:59:06 -0700
committerPaul Eggert2018-09-22 09:01:26 -0700
commit0b36041d2a528419982a19940573783ff318c0d4 (patch)
tree0b66b534356f9e4ed7cb39601f6d29492cbea104
parent596ccc087c8f844f81b075da643e5c554a8de9d6 (diff)
downloademacs-0b36041d2a528419982a19940573783ff318c0d4.tar.gz
emacs-0b36041d2a528419982a19940573783ff318c0d4.zip
Round bignums consistently with other integers
* src/bignum.c (mpz_bufsize): New function. (bignum_bufsize): Use it. (mpz_get_d_rounded): New function. (bignum_to_double): Use it. * src/bignum.c (bignum_to_double): * src/data.c (bignum_arith_driver): When converting bignums to double, round instead of truncating, to be consistent with what happens with fixnums. * test/src/floatfns-tests.el (bignum-to-float): Test rounding.
-rw-r--r--src/bignum.c34
-rw-r--r--src/bignum.h1
-rw-r--r--src/data.c2
-rw-r--r--test/src/floatfns-tests.el6
4 files changed, 39 insertions, 4 deletions
diff --git a/src/bignum.c b/src/bignum.c
index 5e86c404b70..1e78d981b7d 100644
--- a/src/bignum.c
+++ b/src/bignum.c
@@ -62,7 +62,7 @@ init_bignum (void)
62double 62double
63bignum_to_double (Lisp_Object n) 63bignum_to_double (Lisp_Object n)
64{ 64{
65 return mpz_get_d (XBIGNUM (n)->value); 65 return mpz_get_d_rounded (XBIGNUM (n)->value);
66} 66}
67 67
68/* Return D, converted to a Lisp integer. Discard any fraction. 68/* Return D, converted to a Lisp integer. Discard any fraction.
@@ -251,12 +251,40 @@ bignum_to_uintmax (Lisp_Object x)
251} 251}
252 252
253/* Yield an upper bound on the buffer size needed to contain a C 253/* Yield an upper bound on the buffer size needed to contain a C
254 string representing the bignum NUM in base BASE. This includes any 254 string representing the NUM in base BASE. This includes any
255 preceding '-' and the terminating null. */ 255 preceding '-' and the terminating null. */
256static ptrdiff_t
257mpz_bufsize (mpz_t const num, int base)
258{
259 return mpz_sizeinbase (num, base) + 2;
260}
256ptrdiff_t 261ptrdiff_t
257bignum_bufsize (Lisp_Object num, int base) 262bignum_bufsize (Lisp_Object num, int base)
258{ 263{
259 return mpz_sizeinbase (XBIGNUM (num)->value, base) + 2; 264 return mpz_bufsize (XBIGNUM (num)->value, base);
265}
266
267/* Convert NUM to a nearest double, as opposed to mpz_get_d which
268 truncates toward zero. */
269double
270mpz_get_d_rounded (mpz_t const num)
271{
272 ptrdiff_t size = mpz_bufsize (num, 10);
273
274 /* Use mpz_get_d as a shortcut for a bignum so small that rounding
275 errors cannot occur, which is possible if EMACS_INT (not counting
276 sign) has fewer bits than a double significand. */
277 if (! ((FLT_RADIX == 2 && DBL_MANT_DIG <= FIXNUM_BITS - 1)
278 || (FLT_RADIX == 16 && DBL_MANT_DIG * 4 <= FIXNUM_BITS - 1))
279 && size <= DBL_DIG + 2)
280 return mpz_get_d (num);
281
282 USE_SAFE_ALLOCA;
283 char *buf = SAFE_ALLOCA (size);
284 mpz_get_str (buf, 10, num);
285 double result = strtod (buf, NULL);
286 SAFE_FREE ();
287 return result;
260} 288}
261 289
262/* Store into BUF (of size SIZE) the value of NUM as a base-BASE string. 290/* Store into BUF (of size SIZE) the value of NUM as a base-BASE string.
diff --git a/src/bignum.h b/src/bignum.h
index 65515493436..e9cd5c07635 100644
--- a/src/bignum.h
+++ b/src/bignum.h
@@ -46,6 +46,7 @@ extern mpz_t mpz[4];
46extern void init_bignum (void); 46extern void init_bignum (void);
47extern Lisp_Object make_integer_mpz (void); 47extern Lisp_Object make_integer_mpz (void);
48extern void mpz_set_intmax_slow (mpz_t, intmax_t) ARG_NONNULL ((1)); 48extern void mpz_set_intmax_slow (mpz_t, intmax_t) ARG_NONNULL ((1));
49extern double mpz_get_d_rounded (mpz_t const);
49 50
50INLINE_HEADER_BEGIN 51INLINE_HEADER_BEGIN
51 52
diff --git a/src/data.c b/src/data.c
index cc080372d8b..750d494b83a 100644
--- a/src/data.c
+++ b/src/data.c
@@ -2921,7 +2921,7 @@ bignum_arith_driver (enum arithop code, ptrdiff_t nargs, Lisp_Object *args,
2921 CHECK_NUMBER_COERCE_MARKER (val); 2921 CHECK_NUMBER_COERCE_MARKER (val);
2922 if (FLOATP (val)) 2922 if (FLOATP (val))
2923 return float_arith_driver (code, nargs, args, argnum, 2923 return float_arith_driver (code, nargs, args, argnum,
2924 mpz_get_d (*accum), val); 2924 mpz_get_d_rounded (*accum), val);
2925 } 2925 }
2926} 2926}
2927 2927
diff --git a/test/src/floatfns-tests.el b/test/src/floatfns-tests.el
index 14576b603c0..61b1c25743d 100644
--- a/test/src/floatfns-tests.el
+++ b/test/src/floatfns-tests.el
@@ -35,6 +35,12 @@
35 (should-error (fround 0) :type 'wrong-type-argument)) 35 (should-error (fround 0) :type 'wrong-type-argument))
36 36
37(ert-deftest bignum-to-float () 37(ert-deftest bignum-to-float ()
38 ;; 122 because we want to go as big as possible to provoke a rounding error,
39 ;; but not too big: 2**122 < 10**37 < 2**123, and the C standard says
40 ;; 10**37 <= DBL_MAX so 2**122 cannot overflow as a double.
41 (let ((a (1- (ash 1 122))))
42 (should (or (eql a (1- (floor (float a))))
43 (eql a (floor (float a))))))
38 (should (eql (float (+ most-positive-fixnum 1)) 44 (should (eql (float (+ most-positive-fixnum 1))
39 (+ (float most-positive-fixnum) 1)))) 45 (+ (float most-positive-fixnum) 1))))
40 46