Skip to content

Commit 8841f88

Browse files
committed
[Bug #21163] Fix hexadecimal float conversion
1 parent 6bad47a commit 8841f88

File tree

2 files changed

+33
-4
lines changed

2 files changed

+33
-4
lines changed

object.c

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3405,6 +3405,13 @@ rb_f_integer(rb_execution_context_t *ec, VALUE obj, VALUE arg, VALUE base, VALUE
34053405
return rb_convert_to_integer(arg, NUM2INT(base), exc);
34063406
}
34073407

3408+
static bool
3409+
is_digit_char(unsigned char c, int base)
3410+
{
3411+
int i = ruby_digit36_to_number_table[c];
3412+
return (i >= 0 && i < base);
3413+
}
3414+
34083415
static double
34093416
rb_cstr_to_dbl_raise(const char *p, rb_encoding *enc, int badcheck, int raise, int *error)
34103417
{
@@ -3446,30 +3453,48 @@ rb_cstr_to_dbl_raise(const char *p, rb_encoding *enc, int badcheck, int raise, i
34463453
char *e = init_e;
34473454
char prev = 0;
34483455
int dot_seen = FALSE;
3456+
int base = 10;
3457+
char exp_letter = 'e';
34493458

34503459
switch (*p) {case '+': case '-': prev = *n++ = *p++;}
34513460
if (*p == '0') {
34523461
prev = *n++ = '0';
3453-
while (*++p == '0');
3462+
switch (*++p) {
3463+
case 'x': case 'X':
3464+
prev = *n++ = 'x';
3465+
base = 16;
3466+
exp_letter = 'p';
3467+
if (*++p != '0') break;
3468+
/* fallthrough */
3469+
case '0': /* squeeze successive zeros */
3470+
while (*++p == '0');
3471+
break;
3472+
}
34543473
}
34553474
while (p < end && n < e) prev = *n++ = *p++;
34563475
while (*p) {
34573476
if (*p == '_') {
34583477
/* remove an underscore between digits */
3459-
if (n == buf || !ISDIGIT(prev) || (++p, !ISDIGIT(*p))) {
3478+
if (n == buf ||
3479+
!is_digit_char(prev, base) ||
3480+
!is_digit_char(*++p, base)) {
34603481
if (badcheck) goto bad;
34613482
break;
34623483
}
34633484
}
34643485
prev = *p++;
3465-
if (e == init_e && (prev == 'e' || prev == 'E' || prev == 'p' || prev == 'P')) {
3486+
if (e == init_e && (rb_tolower(prev) == exp_letter)) {
34663487
e = buf + sizeof(buf) - 1;
34673488
*n++ = prev;
34683489
switch (*p) {case '+': case '-': prev = *n++ = *p++;}
34693490
if (*p == '0') {
34703491
prev = *n++ = '0';
34713492
while (*++p == '0');
34723493
}
3494+
3495+
/* reset base to decimal for underscore check of
3496+
* binary exponent part */
3497+
base = 10;
34733498
continue;
34743499
}
34753500
else if (ISSPACE(prev)) {
@@ -3479,7 +3504,7 @@ rb_cstr_to_dbl_raise(const char *p, rb_encoding *enc, int badcheck, int raise, i
34793504
break;
34803505
}
34813506
}
3482-
else if (prev == '.' ? dot_seen++ : !ISDIGIT(prev)) {
3507+
else if (prev == '.' ? dot_seen++ : !is_digit_char(prev, base)) {
34833508
if (badcheck) goto bad;
34843509
break;
34853510
}

test/ruby/test_float.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,10 @@ def test_Float
838838
assert_equal(15, Float('0xf.p0'))
839839
assert_equal(15.9375, Float('0xf.f'))
840840
assert_raise(ArgumentError) { Float('0xf.fp') }
841+
assert_equal(0x10a, Float("0x1_0a"))
842+
assert_equal(1.625, Float("0x1.a_0"))
843+
assert_equal(3.25, Float("0x1.ap0_1"))
844+
assert_raise(ArgumentError) { Float("0x1.ap0a") }
841845
begin
842846
verbose_bak, $VERBOSE = $VERBOSE, nil
843847
assert_equal(Float::INFINITY, Float('0xf.fp1000000000000000'))

0 commit comments

Comments
 (0)