From 0a0089fc11fa430a5f2608b8082019641afdadad Mon Sep 17 00:00:00 2001 From: Peter Bindels Date: Sat, 17 Jul 2021 22:49:37 +0200 Subject: 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 --- Userland/Libraries/LibC/stdlib.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'Userland/Libraries/LibC') 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; } -- cgit v1.2.3