Commit 84f39d33 authored by Paul Eggert's avatar Paul Eggert

Export converting mpz to [u]intmax

This refactoring will help improve timestamp handling later
(Bug#32902).
* src/bignum.c (mpz_set_uintmax): Move to bignum.h,
and make inline.
(mpz_set_uintmax_slow): Now extern.
(mpz_to_intmax, mpz_to_uintmax): New functions, with
implementation taken from the old bignum_to_intmax
and bignum_to_uintmax.
(bignum_to_intmax, bignum_to_uintmax): Use them.
parent 0faad0a0
......@@ -101,18 +101,6 @@ make_bignum (void)
return make_bignum_bits (mpz_sizeinbase (mpz[0], 2));
}
static void mpz_set_uintmax_slow (mpz_t, uintmax_t);
/* Set RESULT to V. */
static void
mpz_set_uintmax (mpz_t result, uintmax_t v)
{
if (v <= ULONG_MAX)
mpz_set_ui (result, v);
else
mpz_set_uintmax_slow (result, v);
}
/* Return a Lisp integer equal to N, which must not be in fixnum range. */
Lisp_Object
make_bigint (intmax_t n)
......@@ -183,7 +171,7 @@ mpz_set_intmax_slow (mpz_t result, intmax_t v)
mpz_limbs_finish (result, negative ? -n : n);
}
static void
void
mpz_set_uintmax_slow (mpz_t result, uintmax_t v)
{
int maxlimbs = (UINTMAX_WIDTH + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS;
......@@ -200,13 +188,13 @@ mpz_set_uintmax_slow (mpz_t result, uintmax_t v)
mpz_limbs_finish (result, n);
}
/* Return the value of the bignum X if it fits, 0 otherwise.
A bignum cannot be zero, so 0 indicates failure reliably. */
intmax_t
bignum_to_intmax (Lisp_Object x)
/* If Z fits into *PI, store its value there and return true.
Return false otherwise. */
bool
mpz_to_intmax (mpz_t const z, intmax_t *pi)
{
ptrdiff_t bits = mpz_sizeinbase (XBIGNUM (x)->value, 2);
bool negative = mpz_sgn (XBIGNUM (x)->value) < 0;
ptrdiff_t bits = mpz_sizeinbase (z, 2);
bool negative = mpz_sgn (z) < 0;
if (bits < INTMAX_WIDTH)
{
......@@ -215,39 +203,60 @@ bignum_to_intmax (Lisp_Object x)
do
{
intmax_t limb = mpz_getlimbn (XBIGNUM (x)->value, i++);
intmax_t limb = mpz_getlimbn (z, i++);
v += limb << shift;
shift += GMP_NUMB_BITS;
}
while (shift < bits);
return negative ? -v : v;
*pi = negative ? -v : v;
return true;
}
if (bits == INTMAX_WIDTH && INTMAX_MIN < -INTMAX_MAX && negative
&& mpz_scan1 (z, 0) == INTMAX_WIDTH - 1)
{
*pi = INTMAX_MIN;
return true;
}
return ((bits == INTMAX_WIDTH && INTMAX_MIN < -INTMAX_MAX && negative
&& mpz_scan1 (XBIGNUM (x)->value, 0) == INTMAX_WIDTH - 1)
? INTMAX_MIN : 0);
return false;
}
uintmax_t
bignum_to_uintmax (Lisp_Object x)
bool
mpz_to_uintmax (mpz_t const z, uintmax_t *pi)
{
if (mpz_sgn (z) < 0)
return false;
ptrdiff_t bits = mpz_sizeinbase (z, 2);
if (UINTMAX_WIDTH < bits)
return false;
uintmax_t v = 0;
if (0 <= mpz_sgn (XBIGNUM (x)->value))
int i = 0, shift = 0;
do
{
ptrdiff_t bits = mpz_sizeinbase (XBIGNUM (x)->value, 2);
if (bits <= UINTMAX_WIDTH)
{
int i = 0, shift = 0;
do
{
uintmax_t limb = mpz_getlimbn (XBIGNUM (x)->value, i++);
v += limb << shift;
shift += GMP_NUMB_BITS;
}
while (shift < bits);
}
uintmax_t limb = mpz_getlimbn (z, i++);
v += limb << shift;
shift += GMP_NUMB_BITS;
}
return v;
while (shift < bits);
*pi = v;
return true;
}
/* Return the value of the bignum X if it fits, 0 otherwise.
A bignum cannot be zero, so 0 indicates failure reliably. */
intmax_t
bignum_to_intmax (Lisp_Object x)
{
intmax_t i;
return mpz_to_intmax (XBIGNUM (x)->value, &i) ? i : 0;
}
uintmax_t
bignum_to_uintmax (Lisp_Object x)
{
uintmax_t i;
return mpz_to_uintmax (XBIGNUM (x)->value, &i) ? i : 0;
}
/* Yield an upper bound on the buffer size needed to contain a C
......
......@@ -45,7 +45,10 @@ extern mpz_t mpz[4];
extern void init_bignum (void);
extern Lisp_Object make_integer_mpz (void);
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 double mpz_get_d_rounded (mpz_t const);
INLINE_HEADER_BEGIN
......@@ -68,6 +71,14 @@ mpz_set_intmax (mpz_t result, intmax_t v)
else
mpz_set_intmax_slow (result, v);
}
INLINE void ARG_NONNULL ((1))
mpz_set_uintmax (mpz_t result, uintmax_t v)
{
if (v <= ULONG_MAX)
mpz_set_ui (result, v);
else
mpz_set_uintmax_slow (result, v);
}
/* Return a pointer to an mpz_t that is equal to the Lisp integer I.
If I is a bignum this returns a pointer to I's representation;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment