summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--THANKS1
-rw-r--r--libguile/numbers.c54
-rw-r--r--test-suite/tests/numbers.test10
3 files changed, 46 insertions, 19 deletions
diff --git a/THANKS b/THANKS
index c9a46e2d6..714346d3a 100644
--- a/THANKS
+++ b/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
diff --git a/libguile/numbers.c b/libguile/numbers.c
index fbc6cc8ab..c9fd891b9 100644
--- a/libguile/numbers.c
+++ b/libguile/numbers.c
@@ -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 if (hash_seen)
+ break;
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++;
if (SCM_MOST_POSITIVE_FIXNUM / radix < shift)
diff --git a/test-suite/tests/numbers.test b/test-suite/tests/numbers.test
index 3c3e14fed..3432d7a06 100644
--- a/test-suite/tests/numbers.test
+++ b/test-suite/tests/numbers.test
@@ -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