diff options
author | Peter Bindels <dascandy@gmail.com> | 2021-07-17 22:49:37 +0200 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-07-18 12:45:10 +0100 |
commit | 0a0089fc11fa430a5f2608b8082019641afdadad (patch) | |
tree | da7a6ea2b4d7ba9223a8c7a3a96e8c3d1d2d7263 /Userland/Libraries | |
parent | a205633643a828557c34e19d234a8c996c1f4ee3 (diff) | |
download | serenity-0a0089fc11fa430a5f2608b8082019641afdadad.zip |
LibC strtod: Reduce incremental error to nearly nothing
Instead of scaling by 1/10th N times, scale 10^N and then divide by
that. Avoid doing this beyond double-infinity. This decreases the
progressive error for numbers outside of integer range immensely. Not
a full 100% fix; there is still a single ULP difference detected by a
Javascript test
Diffstat (limited to 'Userland/Libraries')
-rw-r--r-- | Userland/Libraries/LibC/stdlib.cpp | 11 |
1 files changed, 9 insertions, 2 deletions
diff --git a/Userland/Libraries/LibC/stdlib.cpp b/Userland/Libraries/LibC/stdlib.cpp index feae781848..4804678dab 100644 --- a/Userland/Libraries/LibC/stdlib.cpp +++ b/Userland/Libraries/LibC/stdlib.cpp @@ -646,9 +646,15 @@ double strtod(const char* str, char** endptr) // TODO: If `exponent` is large, this could be made faster. double value = digits.number(); + double scale = 1; + if (exponent < 0) { exponent = -exponent; - for (int i = 0; i < exponent; ++i) { + for (int i = 0; i < min(exponent, 300); ++i) { + scale *= base; + } + value /= scale; + for (int i = 300; i < exponent; i++) { value /= base; } if (value == -0.0 || value == +0.0) { @@ -656,8 +662,9 @@ double strtod(const char* str, char** endptr) } } else if (exponent > 0) { for (int i = 0; i < exponent; ++i) { - value *= base; + scale *= base; } + value *= scale; if (value == -__builtin_huge_val() || value == +__builtin_huge_val()) { errno = ERANGE; } |