1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-20 03:30:27 +02:00

Implement scm_integer_length with new integer library

* libguile/integers.c (scm_integer_length_i)
(scm_integer_length_z): New internal functions.
* libguile/integers.h: Declare the new internal functions.
* libguile/numbers.c (scm_integer_length): Use new internal functions.
This commit is contained in:
Andy Wingo 2022-01-04 10:51:20 +01:00
parent 8b99ace658
commit fc4228c196
3 changed files with 42 additions and 33 deletions

View file

@ -2291,3 +2291,39 @@ scm_integer_logcount_z (struct scm_bignum *n)
scm_remember_upto_here_1 (n);
return scm_from_ulong (count);
}
static const char scm_ilentab[] = {
0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4
};
SCM
scm_integer_length_i (scm_t_inum n)
{
unsigned long c = 0;
unsigned int l = 4;
if (n < 0)
n = -1 - n;
while (n)
{
c += 4;
l = scm_ilentab [15 & n];
n >>= 4;
}
return SCM_I_MAKINUM (c - 4 + l);
}
SCM
scm_integer_length_z (struct scm_bignum *n)
{
/* mpz_sizeinbase looks at the absolute value of negatives, whereas we
want a ones-complement. If n is ...111100..00 then mpz_sizeinbase is
1 too big, so check for that and adjust. */
mpz_t zn;
alias_bignum_to_mpz (n, zn);
size_t size = mpz_sizeinbase (zn, 2);
/* If negative and no 0 bits above the lowest 1, adjust result. */
if (mpz_sgn (zn) < 0 && mpz_scan0 (zn, mpz_scan1 (zn, 0)) == ULONG_MAX)
size--;
scm_remember_upto_here_1 (n);
return scm_from_size_t (size);
}

View file

@ -1,7 +1,7 @@
#ifndef SCM_INTEGERS_H
#define SCM_INTEGERS_H
/* Copyright 2021 Free Software Foundation, Inc.
/* Copyright 2021, 2022 Free Software Foundation, Inc.
This file is part of Guile.
@ -141,6 +141,9 @@ SCM_INTERNAL SCM scm_integer_bit_extract_z (struct scm_bignum *n,
SCM_INTERNAL SCM scm_integer_logcount_i (scm_t_inum n);
SCM_INTERNAL SCM scm_integer_logcount_z (struct scm_bignum *n);
SCM_INTERNAL SCM scm_integer_length_i (scm_t_inum n);
SCM_INTERNAL SCM scm_integer_length_z (struct scm_bignum *n);
#endif /* SCM_INTEGERS_H */

View file

@ -3409,12 +3409,6 @@ SCM_DEFINE (scm_logcount, "logcount", 1, 0, 0,
}
#undef FUNC_NAME
static const char scm_ilentab[] = {
0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4
};
SCM_DEFINE (scm_integer_length, "integer-length", 1, 0, 0,
(SCM n),
"Return the number of bits necessary to represent @var{n}.\n"
@ -3430,33 +3424,9 @@ SCM_DEFINE (scm_integer_length, "integer-length", 1, 0, 0,
#define FUNC_NAME s_scm_integer_length
{
if (SCM_I_INUMP (n))
{
unsigned long c = 0;
unsigned int l = 4;
scm_t_inum nn = SCM_I_INUM (n);
if (nn < 0)
nn = -1 - nn;
while (nn)
{
c += 4;
l = scm_ilentab [15 & nn];
nn >>= 4;
}
return SCM_I_MAKINUM (c - 4 + l);
}
return scm_integer_length_i (SCM_I_INUM (n));
else if (SCM_BIGP (n))
{
/* mpz_sizeinbase looks at the absolute value of negatives, whereas we
want a ones-complement. If n is ...111100..00 then mpz_sizeinbase is
1 too big, so check for that and adjust. */
size_t size = mpz_sizeinbase (SCM_I_BIG_MPZ (n), 2);
if (mpz_sgn (SCM_I_BIG_MPZ (n)) < 0
&& mpz_scan0 (SCM_I_BIG_MPZ (n), /* no 0 bits above the lowest 1 */
mpz_scan1 (SCM_I_BIG_MPZ (n), 0)) == ULONG_MAX)
size--;
scm_remember_upto_here_1 (n);
return SCM_I_MAKINUM (size);
}
return scm_integer_length_z (scm_bignum (n));
else
SCM_WRONG_TYPE_ARG (SCM_ARG1, n);
}