Skip to content

Commit 054d093

Browse files
committed
grok_bin_oct_hex: Improve speed, precision
This replaces a floating multiply each loop iteration with an integer increment, plus after the loop completes, a call to ldexp() or pow(). Most of the floating multiplies are done on integral values, so not much precision is lost, but this gets it down to just one precision-losing operation.
1 parent 901167d commit 054d093

File tree

1 file changed

+23
-10
lines changed

1 file changed

+23
-10
lines changed

numeric.c

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -487,9 +487,13 @@ Perl_grok_bin_oct_hex(pTHX_ const char * const start,
487487
break;
488488
}
489489

490-
/* In overflows, this keeps track of how much to multiply the overflowed NV
491-
* by as we continue to parse the remaining digits */
492-
NV factor = 0.0;
490+
/* The loop below accumulates the integral running total of the result,
491+
* digit by digit. If this total overflows, it adds that to an NV
492+
* approximation, and starts looking at the next batch of digits, until
493+
* they overflow, and so on. This variable counts the number of digits
494+
* seen in the current batch. (The initial value is irrelevant, as the
495+
* first batch will end up being multiplied by zero.) */
496+
uint_fast8_t batch_digit_count = 0;
493497

494498
bool overflowed = FALSE;
495499
NV value_nv = 0;
@@ -512,24 +516,29 @@ Perl_grok_bin_oct_hex(pTHX_ const char * const start,
512516
* slowing those down (it does have unnecessary shifts, ANDSs,
513517
* and additions for those) */
514518
value = (value << shift) | XDIGIT_VALUE(*s);
515-
factor *= base;
519+
batch_digit_count++;
516520
continue;
517521
}
518522

519523
/* Bah. We are about to overflow. Instead, add the unoverflowed
520524
* value to an NV that contains an approximation to the correct
521-
* value. Each time through the loop we have increased 'factor' so
522-
* that it gives how much the current approximation needs to
523-
* effectively be shifted to make room for this new value */
524-
value_nv *= factor;
525+
* value. Each time through the loop we have incremented
526+
* 'batch_digit_count' so that it gives how many digits the
527+
* current approximation needs to effectively be shifted to make
528+
* room for this new value */
529+
#ifdef Perl_ldexp
530+
value_nv = Perl_ldexp(value_nv, batch_digit_count * shift);
531+
#else
532+
value_nv *= Perl_pow(base, batch_digit_count);
533+
#endif
525534
value_nv += (NV) value;
526535

527536
/* Then we keep accumulating digits, until all are parsed. We
528537
* start over using the current input value. This will be added to
529538
* 'value_nv' eventually, either when all digits are gone, or we
530539
* have overflowed this fresh start. */
531540
value = XDIGIT_VALUE(*s);
532-
factor = base;
541+
batch_digit_count = 1;
533542

534543
if (! overflowed) {
535544
overflowed = TRUE;
@@ -623,7 +632,11 @@ Perl_grok_bin_oct_hex(pTHX_ const char * const start,
623632
}
624633

625634
/* Overflowed: Calculate the final overflow approximation */
626-
value_nv *= factor;
635+
#ifdef Perl_ldexp
636+
value_nv = Perl_ldexp(value_nv, batch_digit_count * shift);
637+
#else
638+
value_nv *= Perl_pow(base, batch_digit_count);
639+
#endif
627640
value_nv += (NV) value;
628641

629642
output_non_portable(base);

0 commit comments

Comments
 (0)