diff --git a/double-conversion/double-to-string.cc b/double-conversion/double-to-string.cc index ec1d37ec..d0635432 100644 --- a/double-conversion/double-to-string.cc +++ b/double-conversion/double-to-string.cc @@ -410,7 +410,12 @@ void DoubleToStringConverter::DoubleToAscii(double v, return; } - if (v == 0) { + // In SHORTEST_SINGLE mode the value is rendered as a single. A positive + // double below the smallest positive float rounds to +0.0f, which is a + // single zero even though the double is non-zero. Grisu3 would then take the + // boundaries from Single(0.0f), whose NormalizedBoundaries precondition + // (value > 0) is violated, and emit far more digits than the buffer holds. + if (v == 0 || (mode == SHORTEST_SINGLE && static_cast(v) == 0.0f)) { vector[0] = '0'; vector[1] = '\0'; *length = 1; diff --git a/test/cctest/test-dtoa.cc b/test/cctest/test-dtoa.cc index 8a364be4..94771ea6 100644 --- a/test/cctest/test-dtoa.cc +++ b/test/cctest/test-dtoa.cc @@ -93,6 +93,14 @@ TEST(DtoaVariousDoubles) { CHECK_EQ("0", buffer.start()); CHECK_EQ(1, point); + // A positive double below the smallest positive float rounds to +0.0f, so in + // single mode it is a zero. It must render as "0" and not spill past the + // buffer while generating digits at the double's magnitude. + DoubleToAscii(5e-324, SHORTEST_SINGLE, 0, buffer, &sign, &length, &point); + CHECK_EQ(1, length); + CHECK_EQ("0", buffer.start()); + CHECK_EQ(1, point); + DoubleToAscii(0.0, FIXED, 2, buffer, &sign, &length, &point); CHECK_EQ(1, length); CHECK_EQ("0", buffer.start());