diff options
| author | Paul Eggert | 2018-09-22 08:59:06 -0700 |
|---|---|---|
| committer | Paul Eggert | 2018-09-22 09:01:26 -0700 |
| commit | 0b36041d2a528419982a19940573783ff318c0d4 (patch) | |
| tree | 0b66b534356f9e4ed7cb39601f6d29492cbea104 /src | |
| parent | 596ccc087c8f844f81b075da643e5c554a8de9d6 (diff) | |
| download | emacs-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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/bignum.c | 34 | ||||
| -rw-r--r-- | src/bignum.h | 1 | ||||
| -rw-r--r-- | src/data.c | 2 |
3 files changed, 33 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) | |||
| 62 | double | 62 | double |
| 63 | bignum_to_double (Lisp_Object n) | 63 | bignum_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. */ |
| 256 | static ptrdiff_t | ||
| 257 | mpz_bufsize (mpz_t const num, int base) | ||
| 258 | { | ||
| 259 | return mpz_sizeinbase (num, base) + 2; | ||
| 260 | } | ||
| 256 | ptrdiff_t | 261 | ptrdiff_t |
| 257 | bignum_bufsize (Lisp_Object num, int base) | 262 | bignum_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. */ | ||
| 269 | double | ||
| 270 | mpz_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]; | |||
| 46 | extern void init_bignum (void); | 46 | extern void init_bignum (void); |
| 47 | extern Lisp_Object make_integer_mpz (void); | 47 | extern Lisp_Object make_integer_mpz (void); |
| 48 | extern void mpz_set_intmax_slow (mpz_t, intmax_t) ARG_NONNULL ((1)); | 48 | extern void mpz_set_intmax_slow (mpz_t, intmax_t) ARG_NONNULL ((1)); |
| 49 | extern double mpz_get_d_rounded (mpz_t const); | ||
| 49 | 50 | ||
| 50 | INLINE_HEADER_BEGIN | 51 | INLINE_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 | ||