diff options
| author | Paul Eggert | 2017-03-03 09:17:51 -0800 |
|---|---|---|
| committer | Paul Eggert | 2017-03-03 09:19:08 -0800 |
| commit | 74f87fd111904e5156727c72590d6fc4f67e8366 (patch) | |
| tree | f6802878c5105def6d6889d5b8f71e4fe9285b79 /src/floatfns.c | |
| parent | f1fe3fcfc568c1527714ff3a95e678816e2787d4 (diff) | |
| download | emacs-74f87fd111904e5156727c72590d6fc4f67e8366.tar.gz emacs-74f87fd111904e5156727c72590d6fc4f67e8366.zip | |
logb now works correctly on large integers
* admin/merge-gnulib (GNULIB_MODULES): Add count-leading-zeros.
* etc/NEWS: Document the change.
* lib/count-leading-zeros.c, lib/count-leading-zeros.h:
* m4/count-leading-zeros.m4: New files, copied from Gnulib.
* lib/gnulib.mk, m4/gnulib-comp.m4: Regenerate.
* src/floatfns.c: Include count-leading-zeros.h.
(Flogb): Do not convert fixnum to float before taking the log,
as the rounding error can cause the answer to be off by 1.
* src/lisp.h (EMACS_UINT_WIDTH): New constant.
* test/src/floatfns-tests.el (logb-extreme-fixnum): New test.
Diffstat (limited to 'src/floatfns.c')
| -rw-r--r-- | src/floatfns.c | 42 |
1 files changed, 31 insertions, 11 deletions
diff --git a/src/floatfns.c b/src/floatfns.c index dda03698093..94da22a3ba7 100644 --- a/src/floatfns.c +++ b/src/floatfns.c | |||
| @@ -45,6 +45,8 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 45 | 45 | ||
| 46 | #include <math.h> | 46 | #include <math.h> |
| 47 | 47 | ||
| 48 | #include <count-leading-zeros.h> | ||
| 49 | |||
| 48 | /* 'isfinite' and 'isnan' cause build failures on Solaris 10 with the | 50 | /* 'isfinite' and 'isnan' cause build failures on Solaris 10 with the |
| 49 | bundled GCC in c99 mode. Work around the bugs with simple | 51 | bundled GCC in c99 mode. Work around the bugs with simple |
| 50 | implementations that are good enough. */ | 52 | implementations that are good enough. */ |
| @@ -290,28 +292,46 @@ DEFUN ("float", Ffloat, Sfloat, 1, 1, 0, | |||
| 290 | return arg; | 292 | return arg; |
| 291 | } | 293 | } |
| 292 | 294 | ||
| 295 | static int | ||
| 296 | ecount_leading_zeros (EMACS_UINT x) | ||
| 297 | { | ||
| 298 | return (EMACS_UINT_WIDTH == UINT_WIDTH ? count_leading_zeros (x) | ||
| 299 | : EMACS_UINT_WIDTH == ULONG_WIDTH ? count_leading_zeros_l (x) | ||
| 300 | : count_leading_zeros_ll (x)); | ||
| 301 | } | ||
| 302 | |||
| 293 | DEFUN ("logb", Flogb, Slogb, 1, 1, 0, | 303 | DEFUN ("logb", Flogb, Slogb, 1, 1, 0, |
| 294 | doc: /* Returns largest integer <= the base 2 log of the magnitude of ARG. | 304 | doc: /* Returns largest integer <= the base 2 log of the magnitude of ARG. |
| 295 | This is the same as the exponent of a float. */) | 305 | This is the same as the exponent of a float. */) |
| 296 | (Lisp_Object arg) | 306 | (Lisp_Object arg) |
| 297 | { | 307 | { |
| 298 | Lisp_Object val; | ||
| 299 | EMACS_INT value; | 308 | EMACS_INT value; |
| 300 | double f = extract_float (arg); | 309 | CHECK_NUMBER_OR_FLOAT (arg); |
| 301 | 310 | ||
| 302 | if (f == 0.0) | 311 | if (FLOATP (arg)) |
| 303 | value = MOST_NEGATIVE_FIXNUM; | ||
| 304 | else if (isfinite (f)) | ||
| 305 | { | 312 | { |
| 306 | int ivalue; | 313 | double f = XFLOAT_DATA (arg); |
| 307 | frexp (f, &ivalue); | 314 | |
| 308 | value = ivalue - 1; | 315 | if (f == 0) |
| 316 | value = MOST_NEGATIVE_FIXNUM; | ||
| 317 | else if (isfinite (f)) | ||
| 318 | { | ||
| 319 | int ivalue; | ||
| 320 | frexp (f, &ivalue); | ||
| 321 | value = ivalue - 1; | ||
| 322 | } | ||
| 323 | else | ||
| 324 | value = MOST_POSITIVE_FIXNUM; | ||
| 309 | } | 325 | } |
| 310 | else | 326 | else |
| 311 | value = MOST_POSITIVE_FIXNUM; | 327 | { |
| 328 | EMACS_INT i = eabs (XINT (arg)); | ||
| 329 | value = (i == 0 | ||
| 330 | ? MOST_NEGATIVE_FIXNUM | ||
| 331 | : EMACS_UINT_WIDTH - 1 - ecount_leading_zeros (i)); | ||
| 332 | } | ||
| 312 | 333 | ||
| 313 | XSETINT (val, value); | 334 | return make_number (value); |
| 314 | return val; | ||
| 315 | } | 335 | } |
| 316 | 336 | ||
| 317 | 337 | ||