Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(210)

Unified Diff: src/strtod.cc

Issue 3851003: Fix double-rounding in strtod. (Closed)
Patch Set: Created 10 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | test/cctest/test-strtod.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/strtod.cc
diff --git a/src/strtod.cc b/src/strtod.cc
index 68444fc85179b0eed10534019ca8824de6e44258..9af2fc31f34de52536eee0b47847e940066c451c 100644
--- a/src/strtod.cc
+++ b/src/strtod.cc
@@ -85,12 +85,22 @@ static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten);
extern "C" double gay_strtod(const char* s00, const char** se);
static double old_strtod(Vector<const char> buffer, int exponent) {
+ // gay_strtod is broken on Linux,x86. For numbers with few decimal digits
+ // the computation is done using floating-point operations which (on Linux)
+ // are prone to double-rounding errors.
+ // By adding several zeroes to the buffer gay_strtod falls back to a slower
+ // (but correct) algorithm.
+ const int kInsertedZeroesCount = 20;
char gay_buffer[1024];
Vector<char> gay_buffer_vector(gay_buffer, sizeof(gay_buffer));
int pos = 0;
for (int i = 0; i < buffer.length(); ++i) {
gay_buffer_vector[pos++] = buffer[i];
}
+ for (int i = 0; i < kInsertedZeroesCount; ++i) {
+ gay_buffer_vector[pos++] = '0';
+ }
+ exponent -= kInsertedZeroesCount;
gay_buffer_vector[pos++] = 'e';
if (exponent < 0) {
gay_buffer_vector[pos++] = '-';
@@ -139,13 +149,16 @@ uint64_t ReadUint64(Vector<const char> buffer) {
}
-double Strtod(Vector<const char> buffer, int exponent) {
- Vector<const char> left_trimmed = TrimLeadingZeros(buffer);
- Vector<const char> trimmed = TrimTrailingZeros(left_trimmed);
- exponent += left_trimmed.length() - trimmed.length();
- if (trimmed.length() == 0) return 0.0;
- if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) return V8_INFINITY;
- if (exponent + trimmed.length() <= kMinDecimalPower) return 0.0;
+static bool DoubleStrtod(Vector<const char> trimmed,
+ int exponent,
+ double* result) {
+#if defined(V8_TARGET_ARCH_IA32) && !defined(WIN32)
+ // On x86 the floating-point stack can be 64 or 80 bits wide. If it is
+ // 80 bits wide (as is the case on Linux) then double-rounding occurs and the
+ // result is not accurate.
+ // We know that Windows32 uses 64 bits and is therefore accurate.
+ return false;
+#endif
if (trimmed.length() <= kMaxExactDoubleIntegerDecimalDigits) {
// The trimmed input fits into a double.
// If the 10^exponent (resp. 10^-exponent) fits into a double too then we
@@ -155,13 +168,15 @@ double Strtod(Vector<const char> buffer, int exponent) {
// return the best possible approximation.
if (exponent < 0 && -exponent < kExactPowersOfTenSize) {
// 10^-exponent fits into a double.
- double buffer_d = static_cast<double>(ReadUint64(trimmed));
- return buffer_d / exact_powers_of_ten[-exponent];
+ *result = static_cast<double>(ReadUint64(trimmed));
+ *result /= exact_powers_of_ten[-exponent];
+ return true;
}
if (0 <= exponent && exponent < kExactPowersOfTenSize) {
// 10^exponent fits into a double.
- double buffer_d = static_cast<double>(ReadUint64(trimmed));
- return buffer_d * exact_powers_of_ten[exponent];
+ *result = static_cast<double>(ReadUint64(trimmed));
+ *result *= exact_powers_of_ten[exponent];
+ return true;
}
int remaining_digits =
kMaxExactDoubleIntegerDecimalDigits - trimmed.length();
@@ -170,11 +185,27 @@ double Strtod(Vector<const char> buffer, int exponent) {
// The trimmed string was short and we can multiply it with
// 10^remaining_digits. As a result the remaining exponent now fits
// into a double too.
- double buffer_d = static_cast<double>(ReadUint64(trimmed));
- buffer_d *= exact_powers_of_ten[remaining_digits];
- return buffer_d * exact_powers_of_ten[exponent - remaining_digits];
+ *result = static_cast<double>(ReadUint64(trimmed));
+ *result *= exact_powers_of_ten[remaining_digits];
+ *result *= exact_powers_of_ten[exponent - remaining_digits];
+ return true;
}
}
+ return false;
+}
+
+
+double Strtod(Vector<const char> buffer, int exponent) {
+ Vector<const char> left_trimmed = TrimLeadingZeros(buffer);
+ Vector<const char> trimmed = TrimTrailingZeros(left_trimmed);
+ exponent += left_trimmed.length() - trimmed.length();
+ if (trimmed.length() == 0) return 0.0;
+ if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) return V8_INFINITY;
+ if (exponent + trimmed.length() <= kMinDecimalPower) return 0.0;
+ double result;
+ if (DoubleStrtod(trimmed, exponent, &result)) {
+ return result;
+ }
return old_strtod(trimmed, exponent);
}
« no previous file with comments | « no previous file | test/cctest/test-strtod.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698