aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Eggert2018-08-19 01:22:08 -0700
committerPaul Eggert2018-08-19 01:22:25 -0700
commit47b7a5bd492e92dda928843e28a707b9682cb32f (patch)
tree90c26ba233152114aa308fe19a51f38c52735223
parent06b5bcd639bf97fc77dc89dd52f136d4f262e888 (diff)
downloademacs-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.texi2
-rw-r--r--src/floatfns.c47
-rw-r--r--test/src/floatfns-tests.el9
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
1186This function returns @var{x} raised to power @var{y}. If both 1186This function returns @var{x} raised to power @var{y}. If both
1187arguments are integers and @var{y} is nonnegative, the result is an 1187arguments are integers and @var{y} is nonnegative, the result is an
1188integer; in this case, overflow causes truncation, so watch out. 1188integer; in this case, overflow signals an error, so watch out.
1189If @var{x} is a finite negative number and @var{y} is a finite 1189If @var{x} is a finite negative number and @var{y} is a finite
1190non-integer, @code{expt} returns a NaN. 1190non-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)))))