diff options
| author | Paul Eggert | 2019-08-24 15:46:31 -0700 |
|---|---|---|
| committer | Paul Eggert | 2019-08-24 15:55:09 -0700 |
| commit | a050cf80f38e6b9b33745bc62b50dab43cac7a3a (patch) | |
| tree | 163b1d1fb3b78db8672527d42c3afbd66a9313f3 /src/data.c | |
| parent | 2f7ca4020e4f1e30b263758439dba55551f0675d (diff) | |
| download | emacs-a050cf80f38e6b9b33745bc62b50dab43cac7a3a.tar.gz emacs-a050cf80f38e6b9b33745bc62b50dab43cac7a3a.zip | |
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.c | 100 |
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) */) | |||
| 3055 | return arith_driver (Adiv, nargs, args, a); | 3055 | return arith_driver (Adiv, nargs, args, a); |
| 3056 | } | 3056 | } |
| 3057 | 3057 | ||
| 3058 | DEFUN ("%", Frem, Srem, 2, 2, 0, | 3058 | /* Return NUM % DEN (or NUM mod DEN, if MODULO). NUM and DEN must be |
| 3059 | doc: /* Return remainder of X divided by Y. | 3059 | integers. */ |
| 3060 | Both must be integers or markers. */) | ||
| 3061 | (register Lisp_Object x, Lisp_Object y) | ||
| 3062 | { | ||
| 3063 | CHECK_INTEGER_COERCE_MARKER (x); | ||
| 3064 | CHECK_INTEGER_COERCE_MARKER (y); | ||
| 3065 | |||
| 3066 | /* A bignum can never be 0, so don't check that case. */ | ||
| 3067 | if (EQ (y, make_fixnum (0))) | ||
| 3068 | xsignal0 (Qarith_error); | ||
| 3069 | |||
| 3070 | if (FIXNUMP (x) && FIXNUMP (y)) | ||
| 3071 | return make_fixnum (XFIXNUM (x) % XFIXNUM (y)); | ||
| 3072 | else | ||
| 3073 | { | ||
| 3074 | mpz_tdiv_r (mpz[0], | ||
| 3075 | *bignum_integer (&mpz[0], x), | ||
| 3076 | *bignum_integer (&mpz[1], y)); | ||
| 3077 | return make_integer_mpz (); | ||
| 3078 | } | ||
| 3079 | } | ||
| 3080 | |||
| 3081 | /* Return X mod Y. Both must be integers and Y must be nonzero. */ | ||
| 3082 | static Lisp_Object | 3060 | static Lisp_Object |
| 3083 | integer_mod (Lisp_Object x, Lisp_Object y) | 3061 | integer_remainder (Lisp_Object num, Lisp_Object den, bool modulo) |
| 3084 | { | 3062 | { |
| 3085 | if (FIXNUMP (x) && FIXNUMP (y)) | 3063 | if (FIXNUMP (den)) |
| 3086 | { | 3064 | { |
| 3087 | EMACS_INT i1 = XFIXNUM (x), i2 = XFIXNUM (y); | 3065 | EMACS_INT d = XFIXNUM (den); |
| 3066 | if (d == 0) | ||
| 3067 | xsignal0 (Qarith_error); | ||
| 3088 | 3068 | ||
| 3089 | i1 %= i2; | 3069 | EMACS_INT r; |
| 3070 | bool have_r = false; | ||
| 3071 | if (FIXNUMP (num)) | ||
| 3072 | { | ||
| 3073 | r = XFIXNUM (num) % d; | ||
| 3074 | have_r = true; | ||
| 3075 | } | ||
| 3076 | else if (eabs (d) <= ULONG_MAX) | ||
| 3077 | { | ||
| 3078 | mpz_t const *n = xbignum_val (num); | ||
| 3079 | bool neg_n = mpz_sgn (*n) < 0; | ||
| 3080 | r = mpz_tdiv_ui (*n, eabs (d)); | ||
| 3081 | if (neg_n) | ||
| 3082 | r = -r; | ||
| 3083 | have_r = true; | ||
| 3084 | } | ||
| 3090 | 3085 | ||
| 3091 | /* If the "remainder" comes out with the wrong sign, fix it. */ | 3086 | if (have_r) |
| 3092 | if (i2 < 0 ? i1 > 0 : i1 < 0) | 3087 | { |
| 3093 | i1 += i2; | 3088 | /* If MODULO and the remainder has the wrong sign, fix it. */ |
| 3089 | if (modulo && (d < 0 ? r > 0 : r < 0)) | ||
| 3090 | r += d; | ||
| 3094 | 3091 | ||
| 3095 | return make_fixnum (i1); | 3092 | return make_fixnum (r); |
| 3093 | } | ||
| 3096 | } | 3094 | } |
| 3097 | else | ||
| 3098 | { | ||
| 3099 | mpz_t const *ym = bignum_integer (&mpz[1], y); | ||
| 3100 | bool neg_y = mpz_sgn (*ym) < 0; | ||
| 3101 | mpz_tdiv_r (mpz[0], *bignum_integer (&mpz[0], x), *ym); | ||
| 3102 | 3095 | ||
| 3103 | /* Fix the sign if needed. */ | 3096 | mpz_t const *d = bignum_integer (&mpz[1], den); |
| 3104 | int sgn_r = mpz_sgn (mpz[0]); | 3097 | mpz_t *r = &mpz[0]; |
| 3105 | if (neg_y ? sgn_r > 0 : sgn_r < 0) | 3098 | mpz_tdiv_r (*r, *bignum_integer (&mpz[0], num), *d); |
| 3106 | mpz_add (mpz[0], mpz[0], *ym); | ||
| 3107 | 3099 | ||
| 3108 | return make_integer_mpz (); | 3100 | if (modulo) |
| 3101 | { | ||
| 3102 | /* If the remainder has the wrong sign, fix it. */ | ||
| 3103 | int sgn_r = mpz_sgn (*r); | ||
| 3104 | if (mpz_sgn (*d) < 0 ? sgn_r > 0 : sgn_r < 0) | ||
| 3105 | mpz_add (*r, *r, *d); | ||
| 3109 | } | 3106 | } |
| 3107 | |||
| 3108 | return make_integer_mpz (); | ||
| 3109 | } | ||
| 3110 | |||
| 3111 | DEFUN ("%", Frem, Srem, 2, 2, 0, | ||
| 3112 | doc: /* Return remainder of X divided by Y. | ||
| 3113 | Both must be integers or markers. */) | ||
| 3114 | (register Lisp_Object x, Lisp_Object y) | ||
| 3115 | { | ||
| 3116 | CHECK_INTEGER_COERCE_MARKER (x); | ||
| 3117 | CHECK_INTEGER_COERCE_MARKER (y); | ||
| 3118 | return integer_remainder (x, y, false); | ||
| 3110 | } | 3119 | } |
| 3111 | 3120 | ||
| 3112 | DEFUN ("mod", Fmod, Smod, 2, 2, 0, | 3121 | DEFUN ("mod", Fmod, Smod, 2, 2, 0, |
| @@ -3119,12 +3128,7 @@ Both X and Y must be numbers or markers. */) | |||
| 3119 | CHECK_NUMBER_COERCE_MARKER (y); | 3128 | CHECK_NUMBER_COERCE_MARKER (y); |
| 3120 | if (FLOATP (x) || FLOATP (y)) | 3129 | if (FLOATP (x) || FLOATP (y)) |
| 3121 | return fmod_float (x, y); | 3130 | return fmod_float (x, y); |
| 3122 | 3131 | return integer_remainder (x, y, true); | |
| 3123 | /* A bignum can never be 0, so don't check that case. */ | ||
| 3124 | if (EQ (y, make_fixnum (0))) | ||
| 3125 | xsignal0 (Qarith_error); | ||
| 3126 | |||
| 3127 | return integer_mod (x, y); | ||
| 3128 | } | 3132 | } |
| 3129 | 3133 | ||
| 3130 | static Lisp_Object | 3134 | static Lisp_Object |