Parse the fraction tail and the exponent with one subtraction per digit

GCC computed the digit twice, and the in-loop exponent saturation became a
select on the path to the power of ten. Saturate only for more than 18
exponent digits.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Raúl Marín 2026-09-23 22:18:11 +02:00
parent ef5797fa94
commit 1eac92b03f

View File

@ -459,8 +459,13 @@ parse_number_string_impl(UC const *p, UC const *pend,
// for integers with many digits, digit parsing is the primary bottleneck.
loop_parse_if_eight_digits(p, pend, i);
while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - UC('0'));
// One subtraction serves the digit test and the digit (GCC computes it
// twice when written separately); in 64 bits it needs no extension.
while (p != pend) {
uint64_t const digit = uint64_t(*p) - uint64_t(UC('0'));
if (digit > 9) {
break;
}
++p;
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
}
@ -509,13 +514,28 @@ parse_number_string_impl(UC const *p, UC const *pend,
// Otherwise, we will be ignoring the 'e'.
p = location_of_e;
} else {
while ((p != pend) && is_integer(*p)) {
uint8_t digit = uint8_t(*p - UC('0'));
if (exp_number < 0x10000000) {
exp_number = 10 * exp_number + digit;
// Same digit test as the fraction tail. No saturation in the loop: it
// would add a select per digit on the path to the power of ten, and 18
// digits fit in int64_t.
UC const *const exp_start = p;
uint64_t exp_digits = 0;
while (p != pend) {
uint64_t const digit = uint64_t(*p) - uint64_t(UC('0'));
if (digit > 9) {
break;
}
exp_digits = 10 * exp_digits + digit;
++p;
}
exp_number = int64_t(exp_digits);
if fastfloat_unlikely (p - exp_start > 18) {
exp_number = 0;
for (UC const *q = exp_start; q != p; ++q) {
if (exp_number < 0x10000000) {
exp_number = 10 * exp_number + (*q - UC('0'));
}
}
}
if (neg_exp) {
exp_number = -exp_number;
}