Skip to content

Commit

Permalink
Call StrtodTrimmed/StrtofTrimmed from StringToDoubleConverter::String…
Browse files Browse the repository at this point in the history
…ToIeee. (#154)

We know the buffer contains no leading zeros and less than kMaxSignificantDecimalDigits, so we only need to trim trailing zeros and can then call Strto*Trimmed, which is quite a bit faster.
  • Loading branch information
jandem authored Feb 20, 2021
1 parent 95c7131 commit af900a7
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 16 deletions.
10 changes: 8 additions & 2 deletions double-conversion/string-to-double.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<const char> chars(buffer, buffer_pos);
chars = TrimTrailingZeros(chars);
exponent += buffer_pos - chars.length();

double converted;
if (read_as_double) {
converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent);
converted = StrtodTrimmed(chars, exponent);
} else {
converted = Strtof(Vector<const char>(buffer, buffer_pos), exponent);
converted = StrtofTrimmed(chars, exponent);
}
*processed_characters_count = static_cast<int>(current - input);
return sign? -converted: converted;
Expand Down
17 changes: 6 additions & 11 deletions double-conversion/strtod.cc
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,6 @@ static Vector<const char> TrimLeadingZeros(Vector<const char> buffer) {
return Vector<const char>(buffer.start(), 0);
}


static Vector<const char> TrimTrailingZeros(Vector<const char> buffer) {
for (int i = buffer.length() - 1; i >= 0; --i) {
if (buffer[i] != '0') {
return buffer.SubVector(0, i + 1);
}
}
return Vector<const char>(buffer.start(), 0);
}


static void CutToMaxSignificantDigits(Vector<const char> buffer,
int exponent,
char* significant_buffer,
Expand Down Expand Up @@ -536,6 +525,12 @@ float Strtof(Vector<const char> buffer, int exponent) {
TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits,
&trimmed, &updated_exponent);
exponent = updated_exponent;
return StrtofTrimmed(trimmed, exponent);
}

float StrtofTrimmed(Vector<const char> 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);
Expand Down
20 changes: 17 additions & 3 deletions double-conversion/strtod.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,25 @@ double Strtod(Vector<const char> buffer, int exponent);
// contain a dot or a sign. It must not start with '0', and must not be empty.
float Strtof(Vector<const char> 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<const char> 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<const char> trimmed, int exponent);

inline Vector<const char> TrimTrailingZeros(Vector<const char> buffer) {
for (int i = buffer.length() - 1; i >= 0; --i) {
if (buffer[i] != '0') {
return buffer.SubVector(0, i + 1);
}
}
return Vector<const char>(buffer.start(), 0);
}

} // namespace double_conversion

#endif // DOUBLE_CONVERSION_STRTOD_H_
192 changes: 192 additions & 0 deletions test/cctest/test-strtod.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<const char> vector;
Expand Down Expand Up @@ -876,6 +880,194 @@ TEST(Strtof) {

}

TEST(StrtofTrimmed) {
Vector<const char> 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,
Expand Down

0 comments on commit af900a7

Please sign in to comment.