diff options
| author | Paul Eggert | 2018-08-19 01:22:08 -0700 |
|---|---|---|
| committer | Paul Eggert | 2018-08-19 01:22:25 -0700 |
| commit | 47b7a5bd492e92dda928843e28a707b9682cb32f (patch) | |
| tree | 90c26ba233152114aa308fe19a51f38c52735223 /src/floatfns.c | |
| parent | 06b5bcd639bf97fc77dc89dd52f136d4f262e888 (diff) | |
| download | emacs-47b7a5bd492e92dda928843e28a707b9682cb32f.tar.gz emacs-47b7a5bd492e92dda928843e28a707b9682cb32f.zip | |
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, | |||
| 204 | doc: /* Return the exponential ARG1 ** ARG2. */) | 204 | doc: /* Return the exponential ARG1 ** ARG2. */) |
| 205 | (Lisp_Object arg1, Lisp_Object arg2) | 205 | (Lisp_Object arg1, Lisp_Object arg2) |
| 206 | { | 206 | { |
| 207 | CHECK_FIXNUM_OR_FLOAT (arg1); | 207 | CHECK_NUMBER (arg1); |
| 208 | CHECK_FIXNUM_OR_FLOAT (arg2); | 208 | CHECK_NUMBER (arg2); |
| 209 | if (FIXNUMP (arg1) /* common lisp spec */ | 209 | |
| 210 | && FIXNUMP (arg2) /* don't promote, if both are ints, and */ | 210 | /* Common Lisp spec: don't promote if both are integers, and if the |
| 211 | && XFIXNUM (arg2) >= 0) /* we are sure the result is not fractional */ | 211 | result is not fractional. */ |
| 212 | { /* this can be improved by pre-calculating */ | 212 | if (INTEGERP (arg1) && NATNUMP (arg2)) |
| 213 | EMACS_INT y; /* some binary powers of x then accumulating */ | 213 | { |
| 214 | EMACS_UINT acc, x; /* Unsigned so that overflow is well defined. */ | 214 | unsigned long exp; |
| 215 | Lisp_Object val; | 215 | if (RANGED_FIXNUMP (0, arg2, ULONG_MAX)) |
| 216 | 216 | exp = XFIXNUM (arg2); | |
| 217 | x = XFIXNUM (arg1); | 217 | else if (MOST_POSITIVE_FIXNUM < ULONG_MAX && BIGNUMP (arg2) |
| 218 | y = XFIXNUM (arg2); | 218 | && mpz_fits_ulong_p (XBIGNUM (arg2)->value)) |
| 219 | acc = (y & 1 ? x : 1); | 219 | exp = mpz_get_ui (XBIGNUM (arg2)->value); |
| 220 | 220 | else | |
| 221 | while ((y >>= 1) != 0) | 221 | xsignal3 (Qrange_error, build_string ("expt"), arg1, arg2); |
| 222 | |||
| 223 | mpz_t val; | ||
| 224 | mpz_init (val); | ||
| 225 | if (FIXNUMP (arg1)) | ||
| 222 | { | 226 | { |
| 223 | x *= x; | 227 | mpz_set_intmax (val, XFIXNUM (arg1)); |
| 224 | if (y & 1) | 228 | mpz_pow_ui (val, val, exp); |
| 225 | acc *= x; | ||
| 226 | } | 229 | } |
| 227 | XSETINT (val, acc); | 230 | else |
| 228 | return val; | 231 | mpz_pow_ui (val, XBIGNUM (arg1)->value, exp); |
| 232 | Lisp_Object res = make_number (val); | ||
| 233 | mpz_clear (val); | ||
| 234 | return res; | ||
| 229 | } | 235 | } |
| 236 | |||
| 230 | return make_float (pow (XFLOATINT (arg1), XFLOATINT (arg2))); | 237 | return make_float (pow (XFLOATINT (arg1), XFLOATINT (arg2))); |
| 231 | } | 238 | } |
| 232 | 239 | ||