diff options
author | Paul Eggert <eggert@cs.ucla.edu> | 2018-08-19 01:22:08 -0700 |
---|---|---|
committer | Paul Eggert <eggert@cs.ucla.edu> | 2018-08-19 01:22:25 -0700 |
commit | 47b7a5bd492e92dda928843e28a707b9682cb32f (patch) | |
tree | 90c26ba233152114aa308fe19a51f38c52735223 /src/floatfns.c | |
parent | 06b5bcd639bf97fc77dc89dd52f136d4f262e888 (diff) | |
download | emacs-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.c | 47 |
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))); } |