mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-06-30 06:50:31 +02:00
fix string->number for bases > 16
* libguile/numbers.c (scm_iuint2str): Add an assertion on the domain of the radix. Use the number_chars table to write the string, instead of doing strange math. Same effect, though. (mem2uinteger, char_decimal_value): Change logic to allow all ascii alphabetic chars as decimals, not just a-f. Thanks to Nils Gey for the report. * test-suite/tests/numbers.test ("number->string"): Add some tests.
This commit is contained in:
parent
8b755a759e
commit
a6f3af1618
3 changed files with 46 additions and 19 deletions
1
THANKS
1
THANKS
|
@ -44,6 +44,7 @@ For fixes or providing information which led to a fix:
|
|||
Barry Fishman
|
||||
Charles Gagnon
|
||||
Peter Gavin
|
||||
Nils Gey
|
||||
Eric Gillespie, Jr
|
||||
Didier Godefroy
|
||||
Panicz Maciej Godek
|
||||
|
|
|
@ -2402,6 +2402,9 @@ scm_iuint2str (scm_t_uintmax num, int rad, char *p)
|
|||
size_t i;
|
||||
scm_t_uintmax n = num;
|
||||
|
||||
if (rad < 2 || rad > 36)
|
||||
scm_out_of_range ("scm_iuint2str", scm_from_int (rad));
|
||||
|
||||
for (n /= rad; n > 0; n /= rad)
|
||||
j++;
|
||||
|
||||
|
@ -2412,7 +2415,7 @@ scm_iuint2str (scm_t_uintmax num, int rad, char *p)
|
|||
int d = n % rad;
|
||||
|
||||
n /= rad;
|
||||
p[i] = d + ((d < 10) ? '0' : 'a' - 10);
|
||||
p[i] = number_chars[d];
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
@ -2545,11 +2548,26 @@ enum t_exactness {NO_EXACTNESS, INEXACT, EXACT};
|
|||
|
||||
/* R5RS, section 7.1.1, lexical structure of numbers: <uinteger R>. */
|
||||
|
||||
/* In non ASCII-style encodings the following macro might not work. */
|
||||
#define XDIGIT2UINT(d) \
|
||||
(uc_is_property_decimal_digit ((int) (unsigned char) d) \
|
||||
? (d) - '0' \
|
||||
: uc_tolower ((int) (unsigned char) d) - 'a' + 10)
|
||||
/* Caller is responsible for checking that the return value is in range
|
||||
for the given radix, which should be <= 36. */
|
||||
static unsigned int
|
||||
char_decimal_value (scm_t_uint32 c)
|
||||
{
|
||||
/* uc_decimal_value returns -1 on error. When cast to an unsigned int,
|
||||
that's certainly above any valid decimal, so we take advantage of
|
||||
that to elide some tests. */
|
||||
unsigned int d = (unsigned int) uc_decimal_value (c);
|
||||
|
||||
/* If that failed, try extended hexadecimals, then. Only accept ascii
|
||||
hexadecimals. */
|
||||
if (d >= 10U)
|
||||
{
|
||||
c = uc_tolower (c);
|
||||
if (c >= (scm_t_uint32) 'a')
|
||||
d = c - (scm_t_uint32)'a' + 10U;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
static SCM
|
||||
mem2uinteger (SCM mem, unsigned int *p_idx,
|
||||
|
@ -2568,9 +2586,7 @@ mem2uinteger (SCM mem, unsigned int *p_idx,
|
|||
return SCM_BOOL_F;
|
||||
|
||||
c = scm_i_string_ref (mem, idx);
|
||||
if (!uc_is_property_ascii_hex_digit ((scm_t_uint32) c))
|
||||
return SCM_BOOL_F;
|
||||
digit_value = XDIGIT2UINT (c);
|
||||
digit_value = char_decimal_value (c);
|
||||
if (digit_value >= radix)
|
||||
return SCM_BOOL_F;
|
||||
|
||||
|
@ -2579,21 +2595,21 @@ mem2uinteger (SCM mem, unsigned int *p_idx,
|
|||
while (idx != len)
|
||||
{
|
||||
scm_t_wchar c = scm_i_string_ref (mem, idx);
|
||||
if (uc_is_property_ascii_hex_digit ((scm_t_uint32) c))
|
||||
{
|
||||
if (hash_seen)
|
||||
break;
|
||||
digit_value = XDIGIT2UINT (c);
|
||||
if (digit_value >= radix)
|
||||
break;
|
||||
}
|
||||
else if (c == '#')
|
||||
if (c == '#')
|
||||
{
|
||||
hash_seen = 1;
|
||||
digit_value = 0;
|
||||
}
|
||||
else
|
||||
else if (hash_seen)
|
||||
break;
|
||||
else
|
||||
{
|
||||
digit_value = char_decimal_value (c);
|
||||
/* This check catches non-decimals in addition to out-of-range
|
||||
decimals. */
|
||||
if (digit_value >= radix)
|
||||
break;
|
||||
}
|
||||
|
||||
idx++;
|
||||
if (SCM_MOST_POSITIVE_FIXNUM / radix < shift)
|
||||
|
|
|
@ -1288,6 +1288,16 @@
|
|||
(pass-if (string=? (number->string 255.0625 16) "FF.1"))
|
||||
(pass-if (string=? (number->string (/ 1 3) 3) "1/10"))
|
||||
|
||||
(pass-if (string=? (number->string 10) "10"))
|
||||
(pass-if (string=? (number->string 10 11) "A"))
|
||||
(pass-if (string=? (number->string 36 36) "10"))
|
||||
(pass-if (= (num->str->num 36 36) 36))
|
||||
(pass-if (= (string->number "z" 36) 35))
|
||||
(pass-if (= (string->number "Z" 36) 35))
|
||||
(pass-if (not (string->number "Z" 35)))
|
||||
(pass-if (string=? (number->string 35 36) "Z"))
|
||||
(pass-if (= (num->str->num 35 36) 35))
|
||||
|
||||
;; Numeric conversion from decimal is not precise, in its current
|
||||
;; implementation, so 11.333... and 1.324... can't be expected to
|
||||
;; reliably come out to precise values. These tests did actually work
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue