aboutsummaryrefslogtreecommitdiffstats
path: root/src/floatfns.c
diff options
context:
space:
mode:
authorPaul Eggert2018-08-19 01:22:08 -0700
committerPaul Eggert2018-08-19 01:22:25 -0700
commit47b7a5bd492e92dda928843e28a707b9682cb32f (patch)
tree90c26ba233152114aa308fe19a51f38c52735223 /src/floatfns.c
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.
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,
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