summaryrefslogtreecommitdiff
path: root/src/floatfns.c
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2018-08-19 01:22:08 -0700
committerPaul Eggert <eggert@cs.ucla.edu>2018-08-19 01:22:25 -0700
commit47b7a5bd492e92dda928843e28a707b9682cb32f (patch)
tree90c26ba233152114aa308fe19a51f38c52735223 /src/floatfns.c
parent06b5bcd639bf97fc77dc89dd52f136d4f262e888 (diff)
downloademacs-47b7a5bd492e92dda928843e28a707b9682cb32f.tar.gz
Add bignum support to expt
Problem and initial solution reported by Andy Moreton in: https://lists.gnu.org/r/emacs-devel/2018-08/msg00503.html * doc/lispref/numbers.texi (Math Functions): expt integer overflow no longer causes truncation; it now signals an error since bignum overflow is a big deal. * src/floatfns.c (Fexpt): Support bignum arguments. * test/src/floatfns-tests.el (bignum-expt): New test.
Diffstat (limited to 'src/floatfns.c')
-rw-r--r--src/floatfns.c47
1 files changed, 27 insertions, 20 deletions
diff --git a/src/floatfns.c b/src/floatfns.c
index 713d42694ff..54d068c29e5 100644
--- a/src/floatfns.c
+++ b/src/floatfns.c
@@ -204,29 +204,36 @@ DEFUN ("expt", Fexpt, Sexpt, 2, 2, 0,
doc: /* Return the exponential ARG1 ** ARG2. */)
(Lisp_Object arg1, Lisp_Object arg2)
{
- CHECK_FIXNUM_OR_FLOAT (arg1);
- CHECK_FIXNUM_OR_FLOAT (arg2);
- if (FIXNUMP (arg1) /* common lisp spec */
- && FIXNUMP (arg2) /* don't promote, if both are ints, and */
- && XFIXNUM (arg2) >= 0) /* we are sure the result is not fractional */
- { /* this can be improved by pre-calculating */
- EMACS_INT y; /* some binary powers of x then accumulating */
- EMACS_UINT acc, x; /* Unsigned so that overflow is well defined. */
- Lisp_Object val;
-
- x = XFIXNUM (arg1);
- y = XFIXNUM (arg2);
- acc = (y & 1 ? x : 1);
-
- while ((y >>= 1) != 0)
+ CHECK_NUMBER (arg1);
+ CHECK_NUMBER (arg2);
+
+ /* Common Lisp spec: don't promote if both are integers, and if the
+ result is not fractional. */
+ if (INTEGERP (arg1) && NATNUMP (arg2))
+ {
+ unsigned long exp;
+ if (RANGED_FIXNUMP (0, arg2, ULONG_MAX))
+ exp = XFIXNUM (arg2);
+ else if (MOST_POSITIVE_FIXNUM < ULONG_MAX && BIGNUMP (arg2)
+ && mpz_fits_ulong_p (XBIGNUM (arg2)->value))
+ exp = mpz_get_ui (XBIGNUM (arg2)->value);
+ else
+ xsignal3 (Qrange_error, build_string ("expt"), arg1, arg2);
+
+ mpz_t val;
+ mpz_init (val);
+ if (FIXNUMP (arg1))
{
- x *= x;
- if (y & 1)
- acc *= x;
+ mpz_set_intmax (val, XFIXNUM (arg1));
+ mpz_pow_ui (val, val, exp);
}
- XSETINT (val, acc);
- return val;
+ else
+ mpz_pow_ui (val, XBIGNUM (arg1)->value, exp);
+ Lisp_Object res = make_number (val);
+ mpz_clear (val);
+ return res;
}
+
return make_float (pow (XFLOATINT (arg1), XFLOATINT (arg2)));
}