From 74f87fd111904e5156727c72590d6fc4f67e8366 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Fri, 3 Mar 2017 09:17:51 -0800 Subject: 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. --- src/floatfns.c | 42 +++++++++++++++++++++++++++++++----------- src/lisp.h | 6 +++--- 2 files changed, 34 insertions(+), 14 deletions(-) (limited to 'src') 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 . */ #include +#include + /* 'isfinite' and 'isnan' cause build failures on Solaris 10 with the bundled GCC in c99 mode. Work around the bugs with simple implementations that are good enough. */ @@ -290,28 +292,46 @@ DEFUN ("float", Ffloat, Sfloat, 1, 1, 0, return arg; } +static int +ecount_leading_zeros (EMACS_UINT x) +{ + return (EMACS_UINT_WIDTH == UINT_WIDTH ? count_leading_zeros (x) + : EMACS_UINT_WIDTH == ULONG_WIDTH ? count_leading_zeros_l (x) + : count_leading_zeros_ll (x)); +} + DEFUN ("logb", Flogb, Slogb, 1, 1, 0, doc: /* Returns largest integer <= the base 2 log of the magnitude of ARG. This is the same as the exponent of a float. */) (Lisp_Object arg) { - Lisp_Object val; EMACS_INT value; - double f = extract_float (arg); + CHECK_NUMBER_OR_FLOAT (arg); - if (f == 0.0) - value = MOST_NEGATIVE_FIXNUM; - else if (isfinite (f)) + if (FLOATP (arg)) { - int ivalue; - frexp (f, &ivalue); - value = ivalue - 1; + double f = XFLOAT_DATA (arg); + + if (f == 0) + value = MOST_NEGATIVE_FIXNUM; + else if (isfinite (f)) + { + int ivalue; + frexp (f, &ivalue); + value = ivalue - 1; + } + else + value = MOST_POSITIVE_FIXNUM; } else - value = MOST_POSITIVE_FIXNUM; + { + EMACS_INT i = eabs (XINT (arg)); + value = (i == 0 + ? MOST_NEGATIVE_FIXNUM + : EMACS_UINT_WIDTH - 1 - ecount_leading_zeros (i)); + } - XSETINT (val, value); - return val; + return make_number (value); } diff --git a/src/lisp.h b/src/lisp.h index 220188cdb87..6d0b5283356 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -80,19 +80,19 @@ DEFINE_GDB_SYMBOL_END (GCTYPEBITS) # elif INTPTR_MAX <= INT_MAX && !defined WIDE_EMACS_INT typedef int EMACS_INT; typedef unsigned int EMACS_UINT; -enum { EMACS_INT_WIDTH = INT_WIDTH }; +enum { EMACS_INT_WIDTH = INT_WIDTH, EMACS_UINT_WIDTH = UINT_WIDTH }; # define EMACS_INT_MAX INT_MAX # define pI "" # elif INTPTR_MAX <= LONG_MAX && !defined WIDE_EMACS_INT typedef long int EMACS_INT; typedef unsigned long EMACS_UINT; -enum { EMACS_INT_WIDTH = LONG_WIDTH }; +enum { EMACS_INT_WIDTH = LONG_WIDTH, EMACS_UINT_WIDTH = ULONG_WIDTH }; # define EMACS_INT_MAX LONG_MAX # define pI "l" # elif INTPTR_MAX <= LLONG_MAX typedef long long int EMACS_INT; typedef unsigned long long int EMACS_UINT; -enum { EMACS_INT_WIDTH = LLONG_WIDTH }; +enum { EMACS_INT_WIDTH = LLONG_WIDTH, EMACS_UINT_WIDTH = ULLONG_WIDTH }; # define EMACS_INT_MAX LLONG_MAX # ifdef __MINGW32__ # define pI "I64" -- cgit v1.2.1