Browse Source

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
Peter Bindels 4 năm trước cách đây
mục cha
commit
0a0089fc11

+ 9 - 2
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.
     // TODO: If `exponent` is large, this could be made faster.
     double value = digits.number();
     double value = digits.number();
+    double scale = 1;
+
     if (exponent < 0) {
     if (exponent < 0) {
         exponent = -exponent;
         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;
             value /= base;
         }
         }
         if (value == -0.0 || value == +0.0) {
         if (value == -0.0 || value == +0.0) {
@@ -656,8 +662,9 @@ double strtod(const char* str, char** endptr)
         }
         }
     } else if (exponent > 0) {
     } else if (exponent > 0) {
         for (int i = 0; i < exponent; ++i) {
         for (int i = 0; i < exponent; ++i) {
-            value *= base;
+            scale *= base;
         }
         }
+        value *= scale;
         if (value == -__builtin_huge_val() || value == +__builtin_huge_val()) {
         if (value == -__builtin_huge_val() || value == +__builtin_huge_val()) {
             errno = ERANGE;
             errno = ERANGE;
         }
         }