Index: runtime/third_party/double-conversion/src/strtod.cc |
=================================================================== |
--- runtime/third_party/double-conversion/src/strtod.cc (revision 33139) |
+++ runtime/third_party/double-conversion/src/strtod.cc (working copy) |
@@ -31,7 +31,7 @@ |
#include "strtod.h" |
#include "bignum.h" |
#include "cached-powers.h" |
-#include "double.h" |
+#include "ieee.h" |
namespace double_conversion { |
@@ -108,7 +108,7 @@ |
} |
-static void TrimToMaxSignificantDigits(Vector<const char> buffer, |
+static void CutToMaxSignificantDigits(Vector<const char> buffer, |
int exponent, |
char* significant_buffer, |
int* significant_exponent) { |
@@ -125,6 +125,31 @@ |
exponent + (buffer.length() - kMaxSignificantDecimalDigits); |
} |
+ |
+// Trims the buffer and cuts it to at most kMaxSignificantDecimalDigits. |
+// If possible the input-buffer is reused, but if the buffer needs to be |
+// modified (due to cutting), then the input needs to be copied into the |
+// buffer_copy_space. |
+static void TrimAndCut(Vector<const char> buffer, int exponent, |
+ char* buffer_copy_space, int space_size, |
+ Vector<const char>* trimmed, int* updated_exponent) { |
+ Vector<const char> left_trimmed = TrimLeadingZeros(buffer); |
+ Vector<const char> right_trimmed = TrimTrailingZeros(left_trimmed); |
+ exponent += left_trimmed.length() - right_trimmed.length(); |
+ if (right_trimmed.length() > kMaxSignificantDecimalDigits) { |
+ (void) space_size; // Mark variable as used. |
+ ASSERT(space_size >= kMaxSignificantDecimalDigits); |
+ CutToMaxSignificantDigits(right_trimmed, exponent, |
+ buffer_copy_space, updated_exponent); |
+ *trimmed = Vector<const char>(buffer_copy_space, |
+ kMaxSignificantDecimalDigits); |
+ } else { |
+ *trimmed = right_trimmed; |
+ *updated_exponent = exponent; |
+ } |
+} |
+ |
+ |
// Reads digits from the buffer and converts them to a uint64. |
// Reads in as many digits as fit into a uint64. |
// When the string starts with "1844674407370955161" no further digit is read. |
@@ -239,7 +264,6 @@ |
case 7: return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40); |
default: |
UNREACHABLE(); |
- return DiyFp(0, 0); |
} |
} |
@@ -357,22 +381,17 @@ |
} |
-// Returns the correct double for the buffer*10^exponent. |
-// The variable guess should be a close guess that is either the correct double |
-// or its lower neighbor (the nearest double less than the correct one). |
+// Returns |
+// - -1 if buffer*10^exponent < diy_fp. |
+// - 0 if buffer*10^exponent == diy_fp. |
+// - +1 if buffer*10^exponent > diy_fp. |
// Preconditions: |
// buffer.length() + exponent <= kMaxDecimalPower + 1 |
// buffer.length() + exponent > kMinDecimalPower |
// buffer.length() <= kMaxDecimalSignificantDigits |
-static double BignumStrtod(Vector<const char> buffer, |
- int exponent, |
- double guess) { |
- if (guess == Double::Infinity()) { |
- return guess; |
- } |
- |
- DiyFp upper_boundary = Double(guess).UpperBoundary(); |
- |
+static int CompareBufferWithDiyFp(Vector<const char> buffer, |
+ int exponent, |
+ DiyFp diy_fp) { |
ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1); |
ASSERT(buffer.length() + exponent > kMinDecimalPower); |
ASSERT(buffer.length() <= kMaxSignificantDecimalDigits); |
@@ -381,21 +400,65 @@ |
// consume at most one bigit (< 64 bits). |
// ln(10) == 3.3219... |
ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits); |
- Bignum input; |
- Bignum boundary; |
- input.AssignDecimalString(buffer); |
- boundary.AssignUInt64(upper_boundary.f()); |
+ Bignum buffer_bignum; |
+ Bignum diy_fp_bignum; |
+ buffer_bignum.AssignDecimalString(buffer); |
+ diy_fp_bignum.AssignUInt64(diy_fp.f()); |
if (exponent >= 0) { |
- input.MultiplyByPowerOfTen(exponent); |
+ buffer_bignum.MultiplyByPowerOfTen(exponent); |
} else { |
- boundary.MultiplyByPowerOfTen(-exponent); |
+ diy_fp_bignum.MultiplyByPowerOfTen(-exponent); |
} |
- if (upper_boundary.e() > 0) { |
- boundary.ShiftLeft(upper_boundary.e()); |
+ if (diy_fp.e() > 0) { |
+ diy_fp_bignum.ShiftLeft(diy_fp.e()); |
} else { |
- input.ShiftLeft(-upper_boundary.e()); |
+ buffer_bignum.ShiftLeft(-diy_fp.e()); |
} |
- int comparison = Bignum::Compare(input, boundary); |
+ return Bignum::Compare(buffer_bignum, diy_fp_bignum); |
+} |
+ |
+ |
+// Returns true if the guess is the correct double. |
+// Returns false, when guess is either correct or the next-lower double. |
+static bool ComputeGuess(Vector<const char> trimmed, int exponent, |
+ double* guess) { |
+ if (trimmed.length() == 0) { |
+ *guess = 0.0; |
+ return true; |
+ } |
+ if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) { |
+ *guess = Double::Infinity(); |
+ return true; |
+ } |
+ if (exponent + trimmed.length() <= kMinDecimalPower) { |
+ *guess = 0.0; |
+ return true; |
+ } |
+ |
+ if (DoubleStrtod(trimmed, exponent, guess) || |
+ DiyFpStrtod(trimmed, exponent, guess)) { |
+ return true; |
+ } |
+ if (*guess == Double::Infinity()) { |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+double Strtod(Vector<const char> buffer, int exponent) { |
+ char copy_buffer[kMaxSignificantDecimalDigits]; |
+ Vector<const char> trimmed; |
+ int updated_exponent; |
+ TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, |
+ &trimmed, &updated_exponent); |
+ exponent = updated_exponent; |
+ |
+ double guess; |
+ bool is_correct = ComputeGuess(trimmed, exponent, &guess); |
+ if (is_correct) return guess; |
+ |
+ DiyFp upper_boundary = Double(guess).UpperBoundary(); |
+ int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary); |
if (comparison < 0) { |
return guess; |
} else if (comparison > 0) { |
@@ -408,34 +471,85 @@ |
} |
} |
+float Strtof(Vector<const char> buffer, int exponent) { |
+ char copy_buffer[kMaxSignificantDecimalDigits]; |
+ Vector<const char> trimmed; |
+ int updated_exponent; |
+ TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, |
+ &trimmed, &updated_exponent); |
+ exponent = updated_exponent; |
-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 (trimmed.length() > kMaxSignificantDecimalDigits) { |
- char significant_buffer[kMaxSignificantDecimalDigits]; |
- int significant_exponent; |
- TrimToMaxSignificantDigits(trimmed, exponent, |
- significant_buffer, &significant_exponent); |
- return Strtod(Vector<const char>(significant_buffer, |
- kMaxSignificantDecimalDigits), |
- significant_exponent); |
+ double double_guess; |
+ bool is_correct = ComputeGuess(trimmed, exponent, &double_guess); |
+ |
+ float float_guess = static_cast<float>(double_guess); |
+ if (float_guess == double_guess) { |
+ // This shortcut triggers for integer values. |
+ return float_guess; |
} |
- if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) { |
- return Double::Infinity(); |
+ |
+ // We must catch double-rounding. Say the double has been rounded up, and is |
+ // now a boundary of a float, and rounds up again. This is why we have to |
+ // look at previous too. |
+ // Example (in decimal numbers): |
+ // input: 12349 |
+ // high-precision (4 digits): 1235 |
+ // low-precision (3 digits): |
+ // when read from input: 123 |
+ // when rounded from high precision: 124. |
+ // To do this we simply look at the neigbors of the correct result and see |
+ // if they would round to the same float. If the guess is not correct we have |
+ // to look at four values (since two different doubles could be the correct |
+ // double). |
+ |
+ double double_next = Double(double_guess).NextDouble(); |
+ double double_previous = Double(double_guess).PreviousDouble(); |
+ |
+ float f1 = static_cast<float>(double_previous); |
+ float f2 = float_guess; |
+ float f3 = static_cast<float>(double_next); |
+ float f4; |
+ if (is_correct) { |
+ f4 = f3; |
+ } else { |
+ double double_next2 = Double(double_next).NextDouble(); |
+ f4 = static_cast<float>(double_next2); |
} |
- if (exponent + trimmed.length() <= kMinDecimalPower) { |
- return 0.0; |
+ (void) f2; // Mark variable as used. |
+ ASSERT(f1 <= f2 && f2 <= f3 && f3 <= f4); |
+ |
+ // If the guess doesn't lie near a single-precision boundary we can simply |
+ // return its float-value. |
+ if (f1 == f4) { |
+ return float_guess; |
} |
- double guess; |
- if (DoubleStrtod(trimmed, exponent, &guess) || |
- DiyFpStrtod(trimmed, exponent, &guess)) { |
+ ASSERT((f1 != f2 && f2 == f3 && f3 == f4) || |
+ (f1 == f2 && f2 != f3 && f3 == f4) || |
+ (f1 == f2 && f2 == f3 && f3 != f4)); |
+ |
+ // guess and next are the two possible canditates (in the same way that |
+ // double_guess was the lower candidate for a double-precision guess). |
+ float guess = f1; |
+ float next = f4; |
+ DiyFp upper_boundary; |
+ if (guess == 0.0f) { |
+ float min_float = 1e-45f; |
+ upper_boundary = Double(static_cast<double>(min_float) / 2).AsDiyFp(); |
+ } else { |
+ upper_boundary = Single(guess).UpperBoundary(); |
+ } |
+ int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary); |
+ if (comparison < 0) { |
return guess; |
+ } else if (comparison > 0) { |
+ return next; |
+ } else if ((Single(guess).Significand() & 1) == 0) { |
+ // Round towards even. |
+ return guess; |
+ } else { |
+ return next; |
} |
- return BignumStrtod(trimmed, exponent, guess); |
} |
} // namespace double_conversion |