diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a06c710..02c34e32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.0) -project(double-conversion VERSION 3.1.4) +project(double-conversion VERSION 3.1.5) set(headers double-conversion/bignum.h diff --git a/Changelog b/Changelog index 647a0d82..606f47f7 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,8 @@ +2019-05-25: + Fix `0x` for string->double conversion when Hex Floats are allowed. + Avoid integer overflow when exponents for hex floats were too big. + Update version number. + 2019-04-22: Fixed warning in gcc4.9. Thanks to Scott McCaskill (https://github.com/usefulcat) for the patch. diff --git a/double-conversion/double-conversion.cc b/double-conversion/double-conversion.cc index 163d41b8..6da28ed3 100644 --- a/double-conversion/double-conversion.cc +++ b/double-conversion/double-conversion.cc @@ -604,8 +604,8 @@ static bool IsHexFloatString(Iterator start, saw_digit = true; if (Advance(¤t, separator, 16, end)) return false; } - if (!saw_digit) return false; // Only the '.', but no digits. } + if (!saw_digit) return false; if (*current != 'p' && *current != 'P') return false; if (Advance(¤t, separator, 16, end)) return false; if (*current == '+' || *current == '-') { @@ -763,7 +763,11 @@ static double RadixStringToIeee(Iterator* current, } int written_exponent = 0; while (IsDecimalDigitForRadix(**current, 10)) { - written_exponent = 10 * written_exponent + **current - '0'; + // No need to read exponents if they are too big. That could potentially overflow + // the `written_exponent` variable. + if (abs(written_exponent) <= 100 * Double::kMaxExponent) { + written_exponent = 10 * written_exponent + **current - '0'; + } if (Advance(current, separator, radix, end)) break; } if (is_negative) written_exponent = -written_exponent; @@ -899,10 +903,11 @@ double StringToDoubleConverter::StringToIeee( (*current == 'x' || *current == 'X')) { ++current; + if (current == end) return junk_string_value_; // "0x" + bool parse_as_hex_float = (flags_ & ALLOW_HEX_FLOATS) && IsHexFloatString(current, end, separator_, allow_trailing_junk); - if (current == end) return junk_string_value_; // "0x" if (!parse_as_hex_float && !isDigit(*current, 16)) { return junk_string_value_; } diff --git a/double-conversion/ieee.h b/double-conversion/ieee.h index 4a5fe8f9..83274849 100644 --- a/double-conversion/ieee.h +++ b/double-conversion/ieee.h @@ -47,6 +47,8 @@ class Double { static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000); static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit. static const int kSignificandSize = 53; + static const int kExponentBias = 0x3FF + kPhysicalSignificandSize; + static const int kMaxExponent = 0x7FF - kExponentBias; Double() : d64_(0) {} explicit Double(double d) : d64_(double_to_uint64(d)) {} @@ -222,9 +224,7 @@ class Double { } private: - static const int kExponentBias = 0x3FF + kPhysicalSignificandSize; static const int kDenormalExponent = -kExponentBias + 1; - static const int kMaxExponent = 0x7FF - kExponentBias; static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000); static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000); diff --git a/test/cctest/test-conversions.cc b/test/cctest/test-conversions.cc index 878fd0f6..014dd5ef 100644 --- a/test/cctest/test-conversions.cc +++ b/test/cctest/test-conversions.cc @@ -2682,6 +2682,71 @@ TEST(StringToDoubleHexString) { CHECK_EQ(-0.0, StrToD("-0x1p-2000", flags, 0.0, &processed, &all_used)); CHECK(all_used); + + CHECK_EQ(Double::NaN(), StrToD(" ", flags, Double::NaN(), + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0x", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD(" 0x ", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD(" 0x 3", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0x3g", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("x3", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0x3 foo", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD(" 0x3 foo", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("+ 0x3 foo", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("+", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("-", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("- -0x5", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("- +0x5", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("+ +0x5", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0xp1", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::NaN(), StrToD("0x.p1", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Double::Infinity(), StrToD("0x1.p10000000000000000", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(0.0, StrToD("0x1.p-10000000000000000", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); } @@ -4669,6 +4734,156 @@ TEST(StringToFloatHexString) { CHECK_EQ(Single::NaN(), StrToF("x3", flags, 0.0f, &processed, &all_used)); CHECK_EQ(0, processed); + + flags = StringToDoubleConverter::ALLOW_HEX_FLOATS; + + CHECK_EQ(3.0f, StrToF("0x3p0", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(0.0f, StrToF("0x.0p0", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(3.0f, StrToF("0x3.0p0", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(3.0f, StrToF("0x3.p0", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(-5634002804104940178441764864.0f, StrToF("-0x123456789012345678901234p0", + flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(134217728.0f, StrToF("0x8000001p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(134217728.0f, StrToF("0x8000000p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(549755813888.0f, StrToF("0x8000000001p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(549755813888.0f, StrToF("0x8000000000p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(549755879424.0f, StrToF("0x8000008001p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(549755813888.0f, StrToF("0x8000008000p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(549755944960.0f, StrToF("0x8000018001p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(549755944960.0f, StrToF("0x8000018000p0", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(8796093022208.0f, StrToF("0x8000000001p4", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(8796093022208.0f, StrToF("0x8000000000p+4", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(8796094070784.0f, StrToF("0x8000008001p04", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(34359738368.0f, StrToF("0x8000008000p-4", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(34359746560.0f, StrToF("0x8000018001p-04", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(8796095119360.0f, StrToF("0x8000018000p4", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(Single::Infinity(), StrToF("0x1p2000", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(0.0f, StrToF("0x1p-2000", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(-0.0f, StrToF("-0x1p-2000", flags, 0.0, &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(Single::NaN(), StrToF(" ", flags, Single::NaN(), + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0x", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF(" 0x ", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF(" 0x 3", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0x3g", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("x3", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0x3 foo", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF(" 0x3 foo", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("+ 0x3 foo", flags, 0.0, + &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("+", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("-", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("- -0x5", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("- +0x5", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("+ +0x5", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0xp1", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::NaN(), StrToF("0x.p1", flags, 0.0, &processed, &all_used)); + CHECK_EQ(0, processed); + + CHECK_EQ(Single::Infinity(), StrToF("0x1.p10000000000000000", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); + + CHECK_EQ(0.0f, StrToF("0x1.p-10000000000000000", flags, 0.0, + &processed, &all_used)); + CHECK(all_used); }