diff --git a/double-conversion/string-to-double.cc b/double-conversion/string-to-double.cc index 03ad670a..85c3a082 100644 --- a/double-conversion/string-to-double.cc +++ b/double-conversion/string-to-double.cc @@ -729,11 +729,17 @@ double StringToDoubleConverter::StringToIeee( DOUBLE_CONVERSION_ASSERT(buffer_pos < kBufferSize); buffer[buffer_pos] = '\0'; + // Code above ensures there are no leading zeros and the buffer has fewer than + // kMaxSignificantDecimalDigits characters. Trim trailing zeros. + Vector chars(buffer, buffer_pos); + chars = TrimTrailingZeros(chars); + exponent += buffer_pos - chars.length(); + double converted; if (read_as_double) { - converted = Strtod(Vector(buffer, buffer_pos), exponent); + converted = StrtodTrimmed(chars, exponent); } else { - converted = Strtof(Vector(buffer, buffer_pos), exponent); + converted = StrtofTrimmed(chars, exponent); } *processed_characters_count = static_cast(current - input); return sign? -converted: converted; diff --git a/double-conversion/strtod.cc b/double-conversion/strtod.cc index 24fd8599..0cc74951 100644 --- a/double-conversion/strtod.cc +++ b/double-conversion/strtod.cc @@ -101,17 +101,6 @@ static Vector TrimLeadingZeros(Vector buffer) { return Vector(buffer.start(), 0); } - -static Vector TrimTrailingZeros(Vector buffer) { - for (int i = buffer.length() - 1; i >= 0; --i) { - if (buffer[i] != '0') { - return buffer.SubVector(0, i + 1); - } - } - return Vector(buffer.start(), 0); -} - - static void CutToMaxSignificantDigits(Vector buffer, int exponent, char* significant_buffer, @@ -536,6 +525,12 @@ float Strtof(Vector buffer, int exponent) { TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits, &trimmed, &updated_exponent); exponent = updated_exponent; + return StrtofTrimmed(trimmed, exponent); +} + +float StrtofTrimmed(Vector trimmed, int exponent) { + DOUBLE_CONVERSION_ASSERT(trimmed.length() <= kMaxSignificantDecimalDigits); + DOUBLE_CONVERSION_ASSERT(AssertTrimmedDigits(trimmed)); double double_guess; bool is_correct = ComputeGuess(trimmed, exponent, &double_guess); diff --git a/double-conversion/strtod.h b/double-conversion/strtod.h index ff0ee470..77221fb9 100644 --- a/double-conversion/strtod.h +++ b/double-conversion/strtod.h @@ -40,11 +40,25 @@ double Strtod(Vector buffer, int exponent); // contain a dot or a sign. It must not start with '0', and must not be empty. float Strtof(Vector buffer, int exponent); -// For special use cases, the heart of the Strtod() function is also available -// separately, it assumes that 'trimmed' is as produced by TrimAndCut(), i.e. -// no leading or trailing zeros, also no lone zero, and not 'too many' digits. +// Same as Strtod, but assumes that 'trimmed' is already trimmed, as if run +// through TrimAndCut. That is, 'trimmed' must have no leading or trailing +// zeros, must not be a lone zero, and must not have 'too many' digits. double StrtodTrimmed(Vector trimmed, int exponent); +// Same as Strtof, but assumes that 'trimmed' is already trimmed, as if run +// through TrimAndCut. That is, 'trimmed' must have no leading or trailing +// zeros, must not be a lone zero, and must not have 'too many' digits. +float StrtofTrimmed(Vector trimmed, int exponent); + +inline Vector TrimTrailingZeros(Vector buffer) { + for (int i = buffer.length() - 1; i >= 0; --i) { + if (buffer[i] != '0') { + return buffer.SubVector(0, i + 1); + } + } + return Vector(buffer.start(), 0); +} + } // namespace double_conversion #endif // DOUBLE_CONVERSION_STRTOD_H_ diff --git a/test/cctest/test-strtod.cc b/test/cctest/test-strtod.cc index d1295aa5..b5a3bab7 100644 --- a/test/cctest/test-strtod.cc +++ b/test/cctest/test-strtod.cc @@ -55,6 +55,10 @@ static float StrtofChar(const char* str, int exponent) { return Strtof(StringToVector(str), exponent); } +static float StrtofTrimmedChar(const char* str, int exponent) { + return StrtofTrimmed(StringToVector(str), exponent); +} + TEST(Strtod) { Vector vector; @@ -876,6 +880,194 @@ TEST(Strtof) { } +TEST(StrtofTrimmed) { + Vector vector; + + vector = StringToVector("1"); + CHECK_EQ(1.0f, StrtofTrimmed(vector, 0)); + CHECK_EQ(10.0f, StrtofTrimmed(vector, 1)); + CHECK_EQ(100.0f, StrtofTrimmed(vector, 2)); + CHECK_EQ(1e20f, StrtofTrimmed(vector, 20)); + CHECK_EQ(1e22f, StrtofTrimmed(vector, 22)); + CHECK_EQ(1e23f, StrtofTrimmed(vector, 23)); + CHECK_EQ(1e35f, StrtofTrimmed(vector, 35)); + CHECK_EQ(1e36f, StrtofTrimmed(vector, 36)); + CHECK_EQ(1e37f, StrtofTrimmed(vector, 37)); + CHECK_EQ(1e-1f, StrtofTrimmed(vector, -1)); + CHECK_EQ(1e-2f, StrtofTrimmed(vector, -2)); + CHECK_EQ(1e-5f, StrtofTrimmed(vector, -5)); + CHECK_EQ(1e-20f, StrtofTrimmed(vector, -20)); + CHECK_EQ(1e-22f, StrtofTrimmed(vector, -22)); + CHECK_EQ(1e-23f, StrtofTrimmed(vector, -23)); + CHECK_EQ(1e-25f, StrtofTrimmed(vector, -25)); + CHECK_EQ(1e-39f, StrtofTrimmed(vector, -39)); + + vector = StringToVector("2"); + CHECK_EQ(2.0f, StrtofTrimmed(vector, 0)); + CHECK_EQ(20.0f, StrtofTrimmed(vector, 1)); + CHECK_EQ(200.0f, StrtofTrimmed(vector, 2)); + CHECK_EQ(2e20f, StrtofTrimmed(vector, 20)); + CHECK_EQ(2e22f, StrtofTrimmed(vector, 22)); + CHECK_EQ(2e23f, StrtofTrimmed(vector, 23)); + CHECK_EQ(2e35f, StrtofTrimmed(vector, 35)); + CHECK_EQ(2e36f, StrtofTrimmed(vector, 36)); + CHECK_EQ(2e37f, StrtofTrimmed(vector, 37)); + CHECK_EQ(2e-1f, StrtofTrimmed(vector, -1)); + CHECK_EQ(2e-2f, StrtofTrimmed(vector, -2)); + CHECK_EQ(2e-5f, StrtofTrimmed(vector, -5)); + CHECK_EQ(2e-20f, StrtofTrimmed(vector, -20)); + CHECK_EQ(2e-22f, StrtofTrimmed(vector, -22)); + CHECK_EQ(2e-23f, StrtofTrimmed(vector, -23)); + CHECK_EQ(2e-25f, StrtofTrimmed(vector, -25)); + CHECK_EQ(2e-39f, StrtofTrimmed(vector, -39)); + + vector = StringToVector("9"); + CHECK_EQ(9.0f, StrtofTrimmed(vector, 0)); + CHECK_EQ(90.0f, StrtofTrimmed(vector, 1)); + CHECK_EQ(900.0f, StrtofTrimmed(vector, 2)); + CHECK_EQ(9e20f, StrtofTrimmed(vector, 20)); + CHECK_EQ(9e22f, StrtofTrimmed(vector, 22)); + CHECK_EQ(9e23f, StrtofTrimmed(vector, 23)); + CHECK_EQ(9e35f, StrtofTrimmed(vector, 35)); + CHECK_EQ(9e36f, StrtofTrimmed(vector, 36)); + CHECK_EQ(9e37f, StrtofTrimmed(vector, 37)); + CHECK_EQ(9e-1f, StrtofTrimmed(vector, -1)); + CHECK_EQ(9e-2f, StrtofTrimmed(vector, -2)); + CHECK_EQ(9e-5f, StrtofTrimmed(vector, -5)); + CHECK_EQ(9e-20f, StrtofTrimmed(vector, -20)); + CHECK_EQ(9e-22f, StrtofTrimmed(vector, -22)); + CHECK_EQ(9e-23f, StrtofTrimmed(vector, -23)); + CHECK_EQ(9e-25f, StrtofTrimmed(vector, -25)); + CHECK_EQ(9e-39f, StrtofTrimmed(vector, -39)); + + vector = StringToVector("12345"); + CHECK_EQ(12345.0f, StrtofTrimmed(vector, 0)); + CHECK_EQ(123450.0f, StrtofTrimmed(vector, 1)); + CHECK_EQ(1234500.0f, StrtofTrimmed(vector, 2)); + CHECK_EQ(12345e20f, StrtofTrimmed(vector, 20)); + CHECK_EQ(12345e22f, StrtofTrimmed(vector, 22)); + CHECK_EQ(12345e23f, StrtofTrimmed(vector, 23)); + CHECK_EQ(12345e30f, StrtofTrimmed(vector, 30)); + CHECK_EQ(12345e31f, StrtofTrimmed(vector, 31)); + CHECK_EQ(12345e32f, StrtofTrimmed(vector, 32)); + CHECK_EQ(12345e-1f, StrtofTrimmed(vector, -1)); + CHECK_EQ(12345e-2f, StrtofTrimmed(vector, -2)); + CHECK_EQ(12345e-5f, StrtofTrimmed(vector, -5)); + CHECK_EQ(12345e-20f, StrtofTrimmed(vector, -20)); + CHECK_EQ(12345e-22f, StrtofTrimmed(vector, -22)); + CHECK_EQ(12345e-23f, StrtofTrimmed(vector, -23)); + CHECK_EQ(12345e-25f, StrtofTrimmed(vector, -25)); + CHECK_EQ(12345e-39f, StrtofTrimmed(vector, -39)); + + vector = StringToVector("12345678901234"); + CHECK_EQ(12345678901234.0f, StrtofTrimmed(vector, 0)); + CHECK_EQ(123456789012340.0f, StrtofTrimmed(vector, 1)); + CHECK_EQ(1234567890123400.0f, StrtofTrimmed(vector, 2)); + CHECK_EQ(12345678901234e20f, StrtofTrimmed(vector, 20)); + CHECK_EQ(12345678901234e22f, StrtofTrimmed(vector, 22)); + CHECK_EQ(12345678901234e23f, StrtofTrimmed(vector, 23)); + CHECK_EQ(12345678901234e-1f, StrtofTrimmed(vector, -1)); + CHECK_EQ(12345678901234e-2f, StrtofTrimmed(vector, -2)); + CHECK_EQ(12345678901234e-5f, StrtofTrimmed(vector, -5)); + CHECK_EQ(12345678901234e-20f, StrtofTrimmed(vector, -20)); + CHECK_EQ(12345678901234e-22f, StrtofTrimmed(vector, -22)); + CHECK_EQ(12345678901234e-23f, StrtofTrimmed(vector, -23)); + CHECK_EQ(12345678901234e-25f, StrtofTrimmed(vector, -25)); + CHECK_EQ(12345678901234e-39f, StrtofTrimmed(vector, -39)); + + vector = StringToVector("123456789012345"); + CHECK_EQ(123456789012345.0f, StrtofTrimmed(vector, 0)); + CHECK_EQ(1234567890123450.0f, StrtofTrimmed(vector, 1)); + CHECK_EQ(12345678901234500.0f, StrtofTrimmed(vector, 2)); + CHECK_EQ(123456789012345e20f, StrtofTrimmed(vector, 20)); + CHECK_EQ(123456789012345e22f, StrtofTrimmed(vector, 22)); + CHECK_EQ(123456789012345e23f, StrtofTrimmed(vector, 23)); + CHECK_EQ(123456789012345e-1f, StrtofTrimmed(vector, -1)); + CHECK_EQ(123456789012345e-2f, StrtofTrimmed(vector, -2)); + CHECK_EQ(123456789012345e-5f, StrtofTrimmed(vector, -5)); + CHECK_EQ(123456789012345e-20f, StrtofTrimmed(vector, -20)); + CHECK_EQ(123456789012345e-22f, StrtofTrimmed(vector, -22)); + CHECK_EQ(123456789012345e-23f, StrtofTrimmed(vector, -23)); + CHECK_EQ(123456789012345e-25f, StrtofTrimmed(vector, -25)); + CHECK_EQ(123456789012345e-39f, StrtofTrimmed(vector, -39)); + + CHECK_EQ(0.0f, StrtofTrimmedChar("", 1324)); + CHECK_EQ(0.0f, StrtofTrimmedChar("2", -324)); + CHECK_EQ(1e-45f, StrtofTrimmedChar("1", -45)); + // It would be more readable to put non-zero literals on the left side (i.e. + // CHECK_EQ(1e-46, StrtofChar("1", -45))), but then Gcc complains that + // they are truncated to zero. + CHECK_EQ(0.0f, StrtofTrimmedChar("1", -46)); + CHECK_EQ(0.0f, StrtofTrimmedChar("1", -47)); + CHECK_EQ(1e-45f, StrtofTrimmedChar("1", -45)); + CHECK_EQ(1e-45f, StrtofTrimmedChar("8", -46)); + + // It would be more readable to put the literals (and not Double::Infinity()) + // on the left side (i.e. CHECK_EQ(3e38, StrtofChar("3", 38))), but then Gcc + // complains that the floating constant exceeds range of 'double'. + CHECK_EQ(Single::Infinity(), StrtofTrimmedChar("3", 39)); + CHECK_EQ(3e38f, StrtofTrimmedChar("3", 38)); + CHECK_EQ(3401e35f, StrtofTrimmedChar("3401", 35)); + CHECK_EQ(3401e34f, StrtofTrimmedChar("3401", 34)); + CHECK_EQ(34e37f, StrtofTrimmedChar("34", 37)); + CHECK_EQ(3.4028234e+38f, StrtofTrimmedChar("34028235676", 28)); + CHECK_EQ(3.4028234e+38f, StrtofTrimmedChar("34028235677", 28)); + CHECK_EQ(Single::Infinity(), StrtofTrimmedChar("34028235678", 28)); + + // The following number is the result of 89255.0/1e-22. Both floating-point + // numbers can be accurately represented with doubles. However on Linux,x86 + // the floating-point stack is set to 80bits and the double-rounding + // introduces an error. + CHECK_EQ(89255e-22f, StrtofTrimmedChar("89255", -22)); + + // Boundary cases. Boundaries themselves should round to even. + // + // 0x4f012334 = 2166567936 + // next: 2166568192 + // boundary: 2166568064 should round down. + CHECK_EQ(2166567936.0f, StrtofTrimmedChar("2166567936", 0)); + CHECK_EQ(2166568192.0f, StrtofTrimmedChar("2166568192", 0)); + CHECK_EQ(2166567936.0f, StrtofTrimmedChar("2166568064", 0)); + CHECK_EQ(2166567936.0f, StrtofTrimmedChar("216656806399999", -5)); + CHECK_EQ(2166568192.0f, StrtofTrimmedChar("216656806400001", -5)); + // Verify that we don't double round. + // Get the boundary of the boundary. + CHECK_EQ(2.1665680640000002384185791015625e9, 2166568064.0); + // Visual Studio gets this wrong and believes that these two numbers are the + // same doubles. We want to test our conversion and not the compiler. We + // therefore disable the check. +#ifndef _MSC_VER + CHECK(2.16656806400000023841857910156251e9 != 2166568064.0); +#endif + CHECK_EQ(2166568192.0f, StrtofTrimmedChar("21665680640000002384185791015625", -22)); + + // 0x4fffffff = 8589934080 + // next: 8589934592 + // boundary: 8589934336 should round up. + CHECK_EQ(8589934592.0f, StrtofTrimmedChar("8589934592", 0)); + CHECK_EQ(8589934592.0f, StrtofTrimmedChar("8589934336", 0)); + CHECK_EQ(8589934080.0f, StrtofTrimmedChar("858993433599999", -5)); + CHECK_EQ(8589934592.0f, StrtofTrimmedChar("858993433600001", -5)); + // Verify that we don't double round. + // Get the boundary of the boundary. + // Visual Studio gets this wrong. To avoid failing tests because of a broken + // compiler we disable the following two tests. They were only testing the + // compiler. The real test is still active. +#ifndef _MSC_VER + CHECK_EQ(8.589934335999999523162841796875e+09, 8589934336.0); + CHECK(8.5899343359999995231628417968749e+09 != 8589934336.0); +#endif + CHECK_EQ(8589934080.0f, StrtofTrimmedChar("8589934335999999523162841796875", -21)); + + // 0x4f000000 = 2147483648 + // next: 2147483904 + // boundary: 2147483776 should round down. + CHECK_EQ(2147483648.0f, StrtofTrimmedChar("2147483648", 0)); + CHECK_EQ(2147483904.0f, StrtofTrimmedChar("2147483904", 0)); + CHECK_EQ(2147483648.0f, StrtofTrimmedChar("2147483776", 0)); + CHECK_EQ(2147483648.0f, StrtofTrimmedChar("214748377599999", -5)); + CHECK_EQ(2147483904.0f, StrtofTrimmedChar("214748377600001", -5)); +} static int CompareBignumToDiyFp(const Bignum& bignum_digits, int bignum_exponent,