summaryrefslogtreecommitdiff
path: root/src/data.c
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2019-08-24 15:46:31 -0700
committerPaul Eggert <eggert@cs.ucla.edu>2019-08-24 15:55:09 -0700
commita050cf80f38e6b9b33745bc62b50dab43cac7a3a (patch)
tree163b1d1fb3b78db8672527d42c3afbd66a9313f3 /src/data.c
parent2f7ca4020e4f1e30b263758439dba55551f0675d (diff)
downloademacs-a050cf80f38e6b9b33745bc62b50dab43cac7a3a.tar.gz
Speed up % and mod with fixnum denom
* src/data.c (integer_remainder): New function. When the numerator is a bignum and the denominator is small, this function uses mpz_tdiv_ui, which should be faster than mpz_tdiv_r. (Frem, Fmod): Use it.
Diffstat (limited to 'src/data.c')
-rw-r--r--src/data.c100
1 files changed, 52 insertions, 48 deletions
diff --git a/src/data.c b/src/data.c
index cb25fce014a..1d9222e75a7 100644
--- a/src/data.c
+++ b/src/data.c
@@ -3055,58 +3055,67 @@ usage: (/ NUMBER &rest DIVISORS) */)
return arith_driver (Adiv, nargs, args, a);
}
-DEFUN ("%", Frem, Srem, 2, 2, 0,
- doc: /* Return remainder of X divided by Y.
-Both must be integers or markers. */)
- (register Lisp_Object x, Lisp_Object y)
-{
- CHECK_INTEGER_COERCE_MARKER (x);
- CHECK_INTEGER_COERCE_MARKER (y);
-
- /* A bignum can never be 0, so don't check that case. */
- if (EQ (y, make_fixnum (0)))
- xsignal0 (Qarith_error);
-
- if (FIXNUMP (x) && FIXNUMP (y))
- return make_fixnum (XFIXNUM (x) % XFIXNUM (y));
- else
- {
- mpz_tdiv_r (mpz[0],
- *bignum_integer (&mpz[0], x),
- *bignum_integer (&mpz[1], y));
- return make_integer_mpz ();
- }
-}
-
-/* Return X mod Y. Both must be integers and Y must be nonzero. */
+/* Return NUM % DEN (or NUM mod DEN, if MODULO). NUM and DEN must be
+ integers. */
static Lisp_Object
-integer_mod (Lisp_Object x, Lisp_Object y)
+integer_remainder (Lisp_Object num, Lisp_Object den, bool modulo)
{
- if (FIXNUMP (x) && FIXNUMP (y))
+ if (FIXNUMP (den))
{
- EMACS_INT i1 = XFIXNUM (x), i2 = XFIXNUM (y);
+ EMACS_INT d = XFIXNUM (den);
+ if (d == 0)
+ xsignal0 (Qarith_error);
- i1 %= i2;
+ EMACS_INT r;
+ bool have_r = false;
+ if (FIXNUMP (num))
+ {
+ r = XFIXNUM (num) % d;
+ have_r = true;
+ }
+ else if (eabs (d) <= ULONG_MAX)
+ {
+ mpz_t const *n = xbignum_val (num);
+ bool neg_n = mpz_sgn (*n) < 0;
+ r = mpz_tdiv_ui (*n, eabs (d));
+ if (neg_n)
+ r = -r;
+ have_r = true;
+ }
- /* If the "remainder" comes out with the wrong sign, fix it. */
- if (i2 < 0 ? i1 > 0 : i1 < 0)
- i1 += i2;
+ if (have_r)
+ {
+ /* If MODULO and the remainder has the wrong sign, fix it. */
+ if (modulo && (d < 0 ? r > 0 : r < 0))
+ r += d;
- return make_fixnum (i1);
+ return make_fixnum (r);
+ }
}
- else
- {
- mpz_t const *ym = bignum_integer (&mpz[1], y);
- bool neg_y = mpz_sgn (*ym) < 0;
- mpz_tdiv_r (mpz[0], *bignum_integer (&mpz[0], x), *ym);
- /* Fix the sign if needed. */
- int sgn_r = mpz_sgn (mpz[0]);
- if (neg_y ? sgn_r > 0 : sgn_r < 0)
- mpz_add (mpz[0], mpz[0], *ym);
+ mpz_t const *d = bignum_integer (&mpz[1], den);
+ mpz_t *r = &mpz[0];
+ mpz_tdiv_r (*r, *bignum_integer (&mpz[0], num), *d);
- return make_integer_mpz ();
+ if (modulo)
+ {
+ /* If the remainder has the wrong sign, fix it. */
+ int sgn_r = mpz_sgn (*r);
+ if (mpz_sgn (*d) < 0 ? sgn_r > 0 : sgn_r < 0)
+ mpz_add (*r, *r, *d);
}
+
+ return make_integer_mpz ();
+}
+
+DEFUN ("%", Frem, Srem, 2, 2, 0,
+ doc: /* Return remainder of X divided by Y.
+Both must be integers or markers. */)
+ (register Lisp_Object x, Lisp_Object y)
+{
+ CHECK_INTEGER_COERCE_MARKER (x);
+ CHECK_INTEGER_COERCE_MARKER (y);
+ return integer_remainder (x, y, false);
}
DEFUN ("mod", Fmod, Smod, 2, 2, 0,
@@ -3119,12 +3128,7 @@ Both X and Y must be numbers or markers. */)
CHECK_NUMBER_COERCE_MARKER (y);
if (FLOATP (x) || FLOATP (y))
return fmod_float (x, y);
-
- /* A bignum can never be 0, so don't check that case. */
- if (EQ (y, make_fixnum (0)))
- xsignal0 (Qarith_error);
-
- return integer_mod (x, y);
+ return integer_remainder (x, y, true);
}
static Lisp_Object