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:
parent
06b5bcd639
commit
47b7a5bd49
@ -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
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
|
@ -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)))))
|
||||
|
Loading…
Reference in New Issue
Block a user