[Bug #21163] Fix hexadecimal float conversion

This commit is contained in:
Nobuyoshi Nakada 2025-03-04 10:22:17 +09:00
parent 6bad47ac6d
commit 8841f885bd
No known key found for this signature in database
GPG Key ID: 3582D74E1FEE4465
Notes: git 2025-03-07 04:30:21 +00:00
2 changed files with 33 additions and 4 deletions

View File

@ -3405,6 +3405,13 @@ rb_f_integer(rb_execution_context_t *ec, VALUE obj, VALUE arg, VALUE base, VALUE
return rb_convert_to_integer(arg, NUM2INT(base), exc);
}
static bool
is_digit_char(unsigned char c, int base)
{
int i = ruby_digit36_to_number_table[c];
return (i >= 0 && i < base);
}
static double
rb_cstr_to_dbl_raise(const char *p, rb_encoding *enc, int badcheck, int raise, int *error)
{
@ -3446,23 +3453,37 @@ rb_cstr_to_dbl_raise(const char *p, rb_encoding *enc, int badcheck, int raise, i
char *e = init_e;
char prev = 0;
int dot_seen = FALSE;
int base = 10;
char exp_letter = 'e';
switch (*p) {case '+': case '-': prev = *n++ = *p++;}
if (*p == '0') {
prev = *n++ = '0';
while (*++p == '0');
switch (*++p) {
case 'x': case 'X':
prev = *n++ = 'x';
base = 16;
exp_letter = 'p';
if (*++p != '0') break;
/* fallthrough */
case '0': /* squeeze successive zeros */
while (*++p == '0');
break;
}
}
while (p < end && n < e) prev = *n++ = *p++;
while (*p) {
if (*p == '_') {
/* remove an underscore between digits */
if (n == buf || !ISDIGIT(prev) || (++p, !ISDIGIT(*p))) {
if (n == buf ||
!is_digit_char(prev, base) ||
!is_digit_char(*++p, base)) {
if (badcheck) goto bad;
break;
}
}
prev = *p++;
if (e == init_e && (prev == 'e' || prev == 'E' || prev == 'p' || prev == 'P')) {
if (e == init_e && (rb_tolower(prev) == exp_letter)) {
e = buf + sizeof(buf) - 1;
*n++ = prev;
switch (*p) {case '+': case '-': prev = *n++ = *p++;}
@ -3470,6 +3491,10 @@ rb_cstr_to_dbl_raise(const char *p, rb_encoding *enc, int badcheck, int raise, i
prev = *n++ = '0';
while (*++p == '0');
}
/* reset base to decimal for underscore check of
* binary exponent part */
base = 10;
continue;
}
else if (ISSPACE(prev)) {
@ -3479,7 +3504,7 @@ rb_cstr_to_dbl_raise(const char *p, rb_encoding *enc, int badcheck, int raise, i
break;
}
}
else if (prev == '.' ? dot_seen++ : !ISDIGIT(prev)) {
else if (prev == '.' ? dot_seen++ : !is_digit_char(prev, base)) {
if (badcheck) goto bad;
break;
}

View File

@ -838,6 +838,10 @@ class TestFloat < Test::Unit::TestCase
assert_equal(15, Float('0xf.p0'))
assert_equal(15.9375, Float('0xf.f'))
assert_raise(ArgumentError) { Float('0xf.fp') }
assert_equal(0x10a, Float("0x1_0a"))
assert_equal(1.625, Float("0x1.a_0"))
assert_equal(3.25, Float("0x1.ap0_1"))
assert_raise(ArgumentError) { Float("0x1.ap0a") }
begin
verbose_bak, $VERBOSE = $VERBOSE, nil
assert_equal(Float::INFINITY, Float('0xf.fp1000000000000000'))