1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-12-04 08:47:11 +00:00

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.
This commit is contained in:
Paul Eggert 2018-08-19 01:22:08 -07:00
parent 06b5bcd639
commit 47b7a5bd49
3 changed files with 35 additions and 19 deletions

View File

@ -1185,7 +1185,7 @@ returns a NaN.
@defun expt x y
This function returns @var{x} raised to power @var{y}. If both
arguments are integers and @var{y} is nonnegative, the result is an
integer; in this case, overflow causes truncation, so watch out.
integer; in this case, overflow signals an error, so watch out.
If @var{x} is a finite negative number and @var{y} is a finite
non-integer, @code{expt} returns a NaN.
@end defun

View File

@ -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;
CHECK_NUMBER (arg1);
CHECK_NUMBER (arg2);
x = XFIXNUM (arg1);
y = XFIXNUM (arg2);
acc = (y & 1 ? x : 1);
/* 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);
while ((y >>= 1) != 0)
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)));
}

View File

@ -42,6 +42,15 @@
(should (= most-positive-fixnum
(- (abs most-negative-fixnum) 1))))
(ert-deftest bignum-expt ()
(dolist (n (list most-positive-fixnum (1+ most-positive-fixnum)
most-negative-fixnum (1- most-negative-fixnum)
-2 -1 0 1 2))
(should (= (expt n 0) 1))
(should (= (expt n 1) n))
(should (= (expt n 2) (* n n)))
(should (= (expt n 3) (* n n n)))))
(ert-deftest bignum-logb ()
(should (= (+ (logb most-positive-fixnum) 1)
(logb (+ most-positive-fixnum 1)))))