summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibC
diff options
context:
space:
mode:
authorPeter Bindels <dascandy@gmail.com>2021-07-17 22:49:37 +0200
committerLinus Groh <mail@linusgroh.de>2021-07-18 12:45:10 +0100
commit0a0089fc11fa430a5f2608b8082019641afdadad (patch)
treeda7a6ea2b4d7ba9223a8c7a3a96e8c3d1d2d7263 /Userland/Libraries/LibC
parenta205633643a828557c34e19d234a8c996c1f4ee3 (diff)
downloadserenity-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/LibC')
-rw-r--r--Userland/Libraries/LibC/stdlib.cpp11
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;
}