[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master 02e637e 2/3: Refactor bignum multiplication, exponentiation
From: |
Paul Eggert |
Subject: |
master 02e637e 2/3: Refactor bignum multiplication, exponentiation |
Date: |
Wed, 13 Nov 2019 16:10:16 -0500 (EST) |
branch: master
commit 02e637ecca3b1419d2a6c433eca72c5728c65051
Author: Paul Eggert <address@hidden>
Commit: Paul Eggert <address@hidden>
Refactor bignum multiplication, exponentiation
This doesn’t alter behavior, and simplifies the next commit.
* src/bignum.c (GMP_NLIMBS_MAX, NLIMBS_LIMIT, emacs_mpz_size)
(emacs_mpz_mul, emacs_mpz_mul_2exp, emacs_mpz_pow_ui): Move here ...
* src/data.c: ... from here.
---
src/bignum.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/bignum.h | 6 +++++
src/data.c | 74 --------------------------------------------------------
3 files changed, 84 insertions(+), 74 deletions(-)
diff --git a/src/bignum.c b/src/bignum.c
index 167b73e..c31cf3d 100644
--- a/src/bignum.c
+++ b/src/bignum.c
@@ -273,6 +273,84 @@ bignum_to_uintmax (Lisp_Object x)
return mpz_to_uintmax (*xbignum_val (x), &i) ? i : 0;
}
+
+/* Multiply and exponentiate mpz_t values without aborting due to size
+ limits. */
+
+/* GMP tests for this value and aborts (!) if it is exceeded.
+ This is as of GMP 6.1.2 (2016); perhaps future versions will differ. */
+enum { GMP_NLIMBS_MAX = min (INT_MAX, ULONG_MAX / GMP_NUMB_BITS) };
+
+/* An upper bound on limb counts, needed to prevent libgmp and/or
+ Emacs from aborting or otherwise misbehaving. This bound applies
+ to estimates of mpz_t sizes before the mpz_t objects are created,
+ as opposed to integer-width which operates on mpz_t values after
+ creation and before conversion to Lisp bignums. */
+enum
+ {
+ NLIMBS_LIMIT = min (min (/* libgmp needs to store limb counts. */
+ GMP_NLIMBS_MAX,
+
+ /* Size calculations need to work. */
+ min (PTRDIFF_MAX, SIZE_MAX) / sizeof (mp_limb_t)),
+
+ /* Emacs puts bit counts into fixnums. */
+ MOST_POSITIVE_FIXNUM / GMP_NUMB_BITS)
+ };
+
+/* Like mpz_size, but tell the compiler the result is a nonnegative int. */
+
+static int
+emacs_mpz_size (mpz_t const op)
+{
+ mp_size_t size = mpz_size (op);
+ eassume (0 <= size && size <= INT_MAX);
+ return size;
+}
+
+/* Wrappers to work around GMP limitations. As of GMP 6.1.2 (2016),
+ the library code aborts when a number is too large. These wrappers
+ avoid the problem for functions that can return numbers much larger
+ than their arguments. For slowly-growing numbers, the integer
+ width checks in bignum.c should suffice. */
+
+void
+emacs_mpz_mul (mpz_t rop, mpz_t const op1, mpz_t const op2)
+{
+ if (NLIMBS_LIMIT - emacs_mpz_size (op1) < emacs_mpz_size (op2))
+ overflow_error ();
+ mpz_mul (rop, op1, op2);
+}
+
+void
+emacs_mpz_mul_2exp (mpz_t rop, mpz_t const op1, EMACS_INT op2)
+{
+ /* Fudge factor derived from GMP 6.1.2, to avoid an abort in
+ mpz_mul_2exp (look for the '+ 1' in its source code). */
+ enum { mul_2exp_extra_limbs = 1 };
+ enum { lim = min (NLIMBS_LIMIT, GMP_NLIMBS_MAX - mul_2exp_extra_limbs) };
+
+ EMACS_INT op2limbs = op2 / GMP_NUMB_BITS;
+ if (lim - emacs_mpz_size (op1) < op2limbs)
+ overflow_error ();
+ mpz_mul_2exp (rop, op1, op2);
+}
+
+void
+emacs_mpz_pow_ui (mpz_t rop, mpz_t const base, unsigned long exp)
+{
+ /* This fudge factor is derived from GMP 6.1.2, to avoid an abort in
+ mpz_n_pow_ui (look for the '5' in its source code). */
+ enum { pow_ui_extra_limbs = 5 };
+ enum { lim = min (NLIMBS_LIMIT, GMP_NLIMBS_MAX - pow_ui_extra_limbs) };
+
+ int nbase = emacs_mpz_size (base), n;
+ if (INT_MULTIPLY_WRAPV (nbase, exp, &n) || lim < n)
+ overflow_error ();
+ mpz_pow_ui (rop, base, exp);
+}
+
+
/* Yield an upper bound on the buffer size needed to contain a C
string representing the NUM in base BASE. This includes any
preceding '-' and the terminating NUL. */
diff --git a/src/bignum.h b/src/bignum.h
index bf7b366..432fcbc 100644
--- a/src/bignum.h
+++ b/src/bignum.h
@@ -49,6 +49,12 @@ extern bool mpz_to_intmax (mpz_t const, intmax_t *)
ARG_NONNULL ((1, 2));
extern bool mpz_to_uintmax (mpz_t const, uintmax_t *) ARG_NONNULL ((1, 2));
extern void mpz_set_intmax_slow (mpz_t, intmax_t) ARG_NONNULL ((1));
extern void mpz_set_uintmax_slow (mpz_t, uintmax_t) ARG_NONNULL ((1));
+extern void emacs_mpz_mul (mpz_t, mpz_t const, mpz_t const)
+ ARG_NONNULL ((1, 2, 3));
+extern void emacs_mpz_mul_2exp (mpz_t, mpz_t const, EMACS_INT)
+ ARG_NONNULL ((1, 2));
+extern void emacs_mpz_pow_ui (mpz_t, mpz_t const, unsigned long)
+ ARG_NONNULL ((1, 2));
extern double mpz_get_d_rounded (mpz_t const);
INLINE_HEADER_BEGIN
diff --git a/src/data.c b/src/data.c
index 649dc17..9efcd72 100644
--- a/src/data.c
+++ b/src/data.c
@@ -2352,80 +2352,6 @@ bool-vector. IDX starts at 0. */)
return newelt;
}
-/* GMP tests for this value and aborts (!) if it is exceeded.
- This is as of GMP 6.1.2 (2016); perhaps future versions will differ. */
-enum { GMP_NLIMBS_MAX = min (INT_MAX, ULONG_MAX / GMP_NUMB_BITS) };
-
-/* An upper bound on limb counts, needed to prevent libgmp and/or
- Emacs from aborting or otherwise misbehaving. This bound applies
- to estimates of mpz_t sizes before the mpz_t objects are created,
- as opposed to integer-width which operates on mpz_t values after
- creation and before conversion to Lisp bignums. */
-enum
- {
- NLIMBS_LIMIT = min (min (/* libgmp needs to store limb counts. */
- GMP_NLIMBS_MAX,
-
- /* Size calculations need to work. */
- min (PTRDIFF_MAX, SIZE_MAX) / sizeof (mp_limb_t)),
-
- /* Emacs puts bit counts into fixnums. */
- MOST_POSITIVE_FIXNUM / GMP_NUMB_BITS)
- };
-
-/* Like mpz_size, but tell the compiler the result is a nonnegative int. */
-
-static int
-emacs_mpz_size (mpz_t const op)
-{
- mp_size_t size = mpz_size (op);
- eassume (0 <= size && size <= INT_MAX);
- return size;
-}
-
-/* Wrappers to work around GMP limitations. As of GMP 6.1.2 (2016),
- the library code aborts when a number is too large. These wrappers
- avoid the problem for functions that can return numbers much larger
- than their arguments. For slowly-growing numbers, the integer
- width checks in bignum.c should suffice. */
-
-static void
-emacs_mpz_mul (mpz_t rop, mpz_t const op1, mpz_t const op2)
-{
- if (NLIMBS_LIMIT - emacs_mpz_size (op1) < emacs_mpz_size (op2))
- overflow_error ();
- mpz_mul (rop, op1, op2);
-}
-
-static void
-emacs_mpz_mul_2exp (mpz_t rop, mpz_t const op1, EMACS_INT op2)
-{
- /* Fudge factor derived from GMP 6.1.2, to avoid an abort in
- mpz_mul_2exp (look for the '+ 1' in its source code). */
- enum { mul_2exp_extra_limbs = 1 };
- enum { lim = min (NLIMBS_LIMIT, GMP_NLIMBS_MAX - mul_2exp_extra_limbs) };
-
- EMACS_INT op2limbs = op2 / GMP_NUMB_BITS;
- if (lim - emacs_mpz_size (op1) < op2limbs)
- overflow_error ();
- mpz_mul_2exp (rop, op1, op2);
-}
-
-static void
-emacs_mpz_pow_ui (mpz_t rop, mpz_t const base, unsigned long exp)
-{
- /* This fudge factor is derived from GMP 6.1.2, to avoid an abort in
- mpz_n_pow_ui (look for the '5' in its source code). */
- enum { pow_ui_extra_limbs = 5 };
- enum { lim = min (NLIMBS_LIMIT, GMP_NLIMBS_MAX - pow_ui_extra_limbs) };
-
- int nbase = emacs_mpz_size (base), n;
- if (INT_MULTIPLY_WRAPV (nbase, exp, &n) || lim < n)
- overflow_error ();
- mpz_pow_ui (rop, base, exp);
-}
-
-
/* Arithmetic functions */
Lisp_Object