1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-04-30 11:50:28 +02:00

Optimize bignum subtraction

* libguile/integers.c (scm_integer_sub_iz):
(scm_integer_sub_zi):
(scm_integer_sub_zz): Optimize to avoid temporary allocations.
This commit is contained in:
Andy Wingo 2022-01-09 22:54:21 +01:00
parent cc52f01aba
commit 210ab8ff76

View file

@ -2951,17 +2951,20 @@ scm_integer_sub_iz (scm_t_inum x, struct scm_bignum *y)
{ {
if (x == 0) if (x == 0)
return scm_integer_negate_z (y); return scm_integer_negate_z (y);
size_t yn = bignum_limb_count (y);
if (yn == 0)
return SCM_I_MAKINUM (x);
mpz_t result, zx, zy; SCM ret;
mpz_init (result); if (bignum_is_negative (y) == (x < 0))
mpz_init_set_si (zx, x); // Magnitude of result smaller than that of y, but assuming y's
alias_bignum_to_mpz (y, zy); // magnitude is greater than x's, keeping y's sign.
mpz_sub (result, zx, zy); ret = do_sub_1 (x > 0, bignum_limbs (y), yn, inum_magnitude (x));
else
// Magnitude increases, same sign as x.
ret = do_add_1 (x < 0, bignum_limbs (y), yn, inum_magnitude (x));
scm_remember_upto_here_1 (y); scm_remember_upto_here_1 (y);
mpz_clear (zx); return ret;
// FIXME: We know that if X is negative, no need to check if
// result is fixable.
return take_mpz (result);
} }
SCM SCM
@ -2969,34 +2972,49 @@ scm_integer_sub_zi (struct scm_bignum *x, scm_t_inum y)
{ {
if (y == 0) if (y == 0)
return scm_from_bignum (x); return scm_from_bignum (x);
if (y < 0) size_t xn = bignum_limb_count (x);
// Assumes that -INUM_MIN can fit in a scm_t_inum, even if that if (xn == 0)
// scm_t_inum is not fixable, and that scm_integer_add_ii can handle return SCM_I_MAKINUM (y);
// scm_t_inum inputs outside the fixable range.
return scm_integer_add_zi (x, -y);
mpz_t result, zx; SCM ret;
mpz_init (result); if (bignum_is_negative (x) == (y < 0))
alias_bignum_to_mpz (x, zx); // Magnitude decreases, but assuming x's magnitude is greater than
mpz_sub_ui (result, zx, y); // y's, not changing sign.
ret = do_sub_1 (y < 0, bignum_limbs (x), xn, inum_magnitude (y));
else
// Magnitude increases, same sign as x.
ret = do_add_1 (bignum_is_negative (x), bignum_limbs (x), xn,
inum_magnitude (y));
scm_remember_upto_here_1 (x); scm_remember_upto_here_1 (x);
// FIXME: We know that if X is negative, no need to check if return ret;
// result is fixable.
return take_mpz (result);
} }
SCM SCM
scm_integer_sub_zz (struct scm_bignum *x, struct scm_bignum *y) scm_integer_sub_zz (struct scm_bignum *x, struct scm_bignum *y)
{ {
mpz_t result, zx, zy; size_t xn = bignum_limb_count (x);
mpz_init (result); size_t yn = bignum_limb_count (y);
alias_bignum_to_mpz (x, zx); if (xn == 0)
alias_bignum_to_mpz (y, zy); return scm_integer_negate_z (y);
mpz_sub (result, zx, zy); if (yn == 0)
return scm_from_bignum (x);
mp_limb_t *xd = bignum_limbs (x);
mp_limb_t *yd = bignum_limbs (y);
SCM ret;
if (bignum_is_negative (x) != bignum_is_negative (y))
// Magnitude increases, same sign as x.
ret = xn < yn
? do_add (bignum_is_negative (x), yd, yn, xd, xn)
: do_add (bignum_is_negative (x), xd, xn, yd, yn);
else
// Magnitude decreases, changing sign if abs(x) < abs(y).
ret = do_cmp (xd, xn, yd, yn) < 0
? do_sub (!bignum_is_negative (x), yd, yn, xd, xn)
: do_sub (bignum_is_negative (x), xd, xn, yd, yn);
scm_remember_upto_here_2 (x, y); scm_remember_upto_here_2 (x, y);
// FIXME: We know that if X and Y have opposite signs, no need to return ret;
// check if result is fixable.
return take_mpz (result);
} }
SCM SCM