1
Fork 0
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:
Andy Wingo 2010-11-15 23:43:30 +01:00
parent 8b755a759e
commit a6f3af1618
3 changed files with 46 additions and 19 deletions

1
THANKS
View file

@ -44,6 +44,7 @@ For fixes or providing information which led to a fix:
Barry Fishman Barry Fishman
Charles Gagnon Charles Gagnon
Peter Gavin Peter Gavin
Nils Gey
Eric Gillespie, Jr Eric Gillespie, Jr
Didier Godefroy Didier Godefroy
Panicz Maciej Godek Panicz Maciej Godek

View file

@ -2402,6 +2402,9 @@ scm_iuint2str (scm_t_uintmax num, int rad, char *p)
size_t i; size_t i;
scm_t_uintmax n = num; 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) for (n /= rad; n > 0; n /= rad)
j++; j++;
@ -2412,7 +2415,7 @@ scm_iuint2str (scm_t_uintmax num, int rad, char *p)
int d = n % rad; int d = n % rad;
n /= rad; n /= rad;
p[i] = d + ((d < 10) ? '0' : 'a' - 10); p[i] = number_chars[d];
} }
return j; return j;
} }
@ -2545,11 +2548,26 @@ enum t_exactness {NO_EXACTNESS, INEXACT, EXACT};
/* R5RS, section 7.1.1, lexical structure of numbers: <uinteger R>. */ /* R5RS, section 7.1.1, lexical structure of numbers: <uinteger R>. */
/* In non ASCII-style encodings the following macro might not work. */ /* Caller is responsible for checking that the return value is in range
#define XDIGIT2UINT(d) \ for the given radix, which should be <= 36. */
(uc_is_property_decimal_digit ((int) (unsigned char) d) \ static unsigned int
? (d) - '0' \ char_decimal_value (scm_t_uint32 c)
: uc_tolower ((int) (unsigned char) d) - 'a' + 10) {
/* 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 static SCM
mem2uinteger (SCM mem, unsigned int *p_idx, mem2uinteger (SCM mem, unsigned int *p_idx,
@ -2568,9 +2586,7 @@ mem2uinteger (SCM mem, unsigned int *p_idx,
return SCM_BOOL_F; return SCM_BOOL_F;
c = scm_i_string_ref (mem, idx); c = scm_i_string_ref (mem, idx);
if (!uc_is_property_ascii_hex_digit ((scm_t_uint32) c)) digit_value = char_decimal_value (c);
return SCM_BOOL_F;
digit_value = XDIGIT2UINT (c);
if (digit_value >= radix) if (digit_value >= radix)
return SCM_BOOL_F; return SCM_BOOL_F;
@ -2579,21 +2595,21 @@ mem2uinteger (SCM mem, unsigned int *p_idx,
while (idx != len) while (idx != len)
{ {
scm_t_wchar c = scm_i_string_ref (mem, idx); scm_t_wchar c = scm_i_string_ref (mem, idx);
if (uc_is_property_ascii_hex_digit ((scm_t_uint32) c)) if (c == '#')
{
if (hash_seen)
break;
digit_value = XDIGIT2UINT (c);
if (digit_value >= radix)
break;
}
else if (c == '#')
{ {
hash_seen = 1; hash_seen = 1;
digit_value = 0; digit_value = 0;
} }
else if (hash_seen)
break;
else else
break; {
digit_value = char_decimal_value (c);
/* This check catches non-decimals in addition to out-of-range
decimals. */
if (digit_value >= radix)
break;
}
idx++; idx++;
if (SCM_MOST_POSITIVE_FIXNUM / radix < shift) if (SCM_MOST_POSITIVE_FIXNUM / radix < shift)

View file

@ -1288,6 +1288,16 @@
(pass-if (string=? (number->string 255.0625 16) "FF.1")) (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 (/ 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 ;; Numeric conversion from decimal is not precise, in its current
;; implementation, so 11.333... and 1.324... can't be expected to ;; implementation, so 11.333... and 1.324... can't be expected to
;; reliably come out to precise values. These tests did actually work ;; reliably come out to precise values. These tests did actually work