1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-20 11:40:18 +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); scm_remember_upto_here_1 (n);
return scm_from_ulong (count); 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 #ifndef SCM_INTEGERS_H
#define 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. 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_i (scm_t_inum n);
SCM_INTERNAL SCM scm_integer_logcount_z (struct scm_bignum *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 */ #endif /* SCM_INTEGERS_H */

View file

@ -3409,12 +3409,6 @@ SCM_DEFINE (scm_logcount, "logcount", 1, 0, 0,
} }
#undef FUNC_NAME #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_DEFINE (scm_integer_length, "integer-length", 1, 0, 0,
(SCM n), (SCM n),
"Return the number of bits necessary to represent @var{n}.\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 #define FUNC_NAME s_scm_integer_length
{ {
if (SCM_I_INUMP (n)) if (SCM_I_INUMP (n))
{ return scm_integer_length_i (SCM_I_INUM (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);
}
else if (SCM_BIGP (n)) else if (SCM_BIGP (n))
{ return scm_integer_length_z (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. */
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);
}
else else
SCM_WRONG_TYPE_ARG (SCM_ARG1, n); SCM_WRONG_TYPE_ARG (SCM_ARG1, n);
} }