summaryrefslogtreecommitdiff
path: root/src/floatfns.c
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2017-03-03 09:17:51 -0800
committerPaul Eggert <eggert@cs.ucla.edu>2017-03-03 09:19:08 -0800
commit74f87fd111904e5156727c72590d6fc4f67e8366 (patch)
treef6802878c5105def6d6889d5b8f71e4fe9285b79 /src/floatfns.c
parentf1fe3fcfc568c1527714ff3a95e678816e2787d4 (diff)
downloademacs-74f87fd111904e5156727c72590d6fc4f67e8366.tar.gz
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.c42
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/>. */
#include <math.h>
+#include <count-leading-zeros.h>
+
/* '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);
}