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 | |
| 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.
| -rw-r--r-- | doc/lispref/numbers.texi | 2 | ||||
| -rw-r--r-- | src/floatfns.c | 47 | ||||
| -rw-r--r-- | test/src/floatfns-tests.el | 9 |
3 files changed, 37 insertions, 21 deletions
diff --git a/doc/lispref/numbers.texi b/doc/lispref/numbers.texi index 74a313e2e10..209e9f139a5 100644 --- a/doc/lispref/numbers.texi +++ b/doc/lispref/numbers.texi | |||
| @@ -1185,7 +1185,7 @@ returns a NaN. | |||
| 1185 | @defun expt x y | 1185 | @defun expt x y |
| 1186 | This function returns @var{x} raised to power @var{y}. If both | 1186 | This function returns @var{x} raised to power @var{y}. If both |
| 1187 | arguments are integers and @var{y} is nonnegative, the result is an | 1187 | arguments are integers and @var{y} is nonnegative, the result is an |
| 1188 | integer; in this case, overflow causes truncation, so watch out. | 1188 | integer; in this case, overflow signals an error, so watch out. |
| 1189 | If @var{x} is a finite negative number and @var{y} is a finite | 1189 | If @var{x} is a finite negative number and @var{y} is a finite |
| 1190 | non-integer, @code{expt} returns a NaN. | 1190 | non-integer, @code{expt} returns a NaN. |
| 1191 | @end defun | 1191 | @end defun |
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 | ||
diff --git a/test/src/floatfns-tests.el b/test/src/floatfns-tests.el index 43a2e278290..e4caaa1e49b 100644 --- a/test/src/floatfns-tests.el +++ b/test/src/floatfns-tests.el | |||
| @@ -42,6 +42,15 @@ | |||
| 42 | (should (= most-positive-fixnum | 42 | (should (= most-positive-fixnum |
| 43 | (- (abs most-negative-fixnum) 1)))) | 43 | (- (abs most-negative-fixnum) 1)))) |
| 44 | 44 | ||
| 45 | (ert-deftest bignum-expt () | ||
| 46 | (dolist (n (list most-positive-fixnum (1+ most-positive-fixnum) | ||
| 47 | most-negative-fixnum (1- most-negative-fixnum) | ||
| 48 | -2 -1 0 1 2)) | ||
| 49 | (should (= (expt n 0) 1)) | ||
| 50 | (should (= (expt n 1) n)) | ||
| 51 | (should (= (expt n 2) (* n n))) | ||
| 52 | (should (= (expt n 3) (* n n n))))) | ||
| 53 | |||
| 45 | (ert-deftest bignum-logb () | 54 | (ert-deftest bignum-logb () |
| 46 | (should (= (+ (logb most-positive-fixnum) 1) | 55 | (should (= (+ (logb most-positive-fixnum) 1) |
| 47 | (logb (+ most-positive-fixnum 1))))) | 56 | (logb (+ most-positive-fixnum 1))))) |