diff --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp --- a/llvm/lib/Support/APFloat.cpp +++ b/llvm/lib/Support/APFloat.cpp @@ -2621,24 +2621,70 @@ } bool IEEEFloat::convertFromStringSpecials(StringRef str) { + const size_t MIN_NAME_SIZE = 3; + + if (str.size() < MIN_NAME_SIZE) + return false; + if (str.equals("inf") || str.equals("INFINITY") || str.equals("+Inf")) { makeInf(false); return true; } - if (str.equals("-inf") || str.equals("-INFINITY") || str.equals("-Inf")) { - makeInf(true); - return true; + bool IsNegative = str.front() == '-'; + if (IsNegative) { + str = str.drop_front(); + if (str.size() < MIN_NAME_SIZE) + return false; + + if (str.equals("inf") || str.equals("INFINITY") || str.equals("Inf")) { + makeInf(true); + return true; + } } - if (str.equals("nan") || str.equals("NaN")) { - makeNaN(false, false); - return true; + // If we have a 's' (or 'S') prefix, then this is a Signaling NaN. + bool IsSignaling = str.front() == 's' || str.front() == 'S'; + if (IsSignaling) { + str = str.drop_front(); + if (str.size() < MIN_NAME_SIZE) + return false; } - if (str.equals("-nan") || str.equals("-NaN")) { - makeNaN(false, true); - return true; + if (str.startswith("nan") || str.startswith("NaN")) { + str = str.drop_front(3); + + // A NaN without payload. + if (str.empty()) { + makeNaN(IsSignaling, IsNegative); + return true; + } + + // Allow the payload to be inside parentheses. + if (str.front() == '(') { + // Parentheses should be balanced (and not empty). + if (str.size() <= 2 || str.back() != ')') + return false; + + str = str.slice(1, str.size() - 1); + } + + // Determine the payload number's radix. + unsigned Radix = 10; + if (str[0] == '0') { + if (str.size() > 1 && tolower(str[1]) == 'x') { + str = str.drop_front(2); + Radix = 16; + } else + Radix = 8; + } + + // Parse the payload and make the NaN. + APInt Payload; + if (!str.getAsInteger(Radix, Payload)) { + makeNaN(IsSignaling, IsNegative, &Payload); + return true; + } } return false; diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp --- a/llvm/unittests/ADT/APFloatTest.cpp +++ b/llvm/unittests/ADT/APFloatTest.cpp @@ -919,6 +919,120 @@ EXPECT_EQ(2.71828, convertToDoubleFromString("2.71828")); } +TEST(APFloatTest, fromStringSpecials) { + const fltSemantics &Sem = APFloat::IEEEdouble(); + const unsigned Precision = 53; + const unsigned PayloadBits = Precision - 2; + uint64_t PayloadMask = (uint64_t(1) << PayloadBits) - uint64_t(1); + + uint64_t NaNPayloads[] = { + 0, + 1, + 123, + 0xDEADBEEF, + uint64_t(-2), + uint64_t(1) << PayloadBits, // overflow bit + uint64_t(1) << (PayloadBits - 1), // signaling bit + uint64_t(1) << (PayloadBits - 2) // highest possible bit + }; + + // Convert payload integer to decimal string representation. + std::string NaNPayloadDecStrings[array_lengthof(NaNPayloads)]; + for (size_t I = 0; I < array_lengthof(NaNPayloads); ++I) + NaNPayloadDecStrings[I] = utostr(NaNPayloads[I]); + + // Convert payload integer to hexadecimal string representation. + std::string NaNPayloadHexStrings[array_lengthof(NaNPayloads)]; + for (size_t I = 0; I < array_lengthof(NaNPayloads); ++I) + NaNPayloadHexStrings[I] = "0x" + utohexstr(NaNPayloads[I]); + + // Fix payloads to expected result. + for (uint64_t &Payload : NaNPayloads) + Payload &= PayloadMask; + + // Signaling NaN must have a non-zero payload. In case a zero payload is + // requested, a default arbitrary payload is set instead. Save this payload + // for testing. + const uint64_t SNaNDefaultPayload = + APFloat::getSNaN(Sem).bitcastToAPInt().getZExtValue() & PayloadMask; + + // Negative sign prefix (or none - for positive). + const char Signs[] = {0, '-'}; + + // "Signaling" prefix (or none - for "Quiet"). + const char NaNTypes[] = {0, 's', 'S'}; + + const StringRef NaNStrings[] = {"nan", "NaN"}; + for (StringRef NaNStr : NaNStrings) + for (char TypeChar : NaNTypes) { + bool Signaling = (TypeChar == 's' || TypeChar == 'S'); + + for (size_t J = 0; J < array_lengthof(NaNPayloads); ++J) { + uint64_t Payload = (Signaling && !NaNPayloads[J]) ? SNaNDefaultPayload + : NaNPayloads[J]; + std::string &PayloadDec = NaNPayloadDecStrings[J]; + std::string &PayloadHex = NaNPayloadHexStrings[J]; + + for (char SignChar : Signs) { + bool Negative = (SignChar == '-'); + + std::string TestStrings[5]; + size_t NumTestStrings = 0; + + std::string Prefix; + if (SignChar) + Prefix += SignChar; + if (TypeChar) + Prefix += TypeChar; + Prefix += NaNStr; + + // Test without any paylod. + if (!Payload) + TestStrings[NumTestStrings++] = Prefix; + + // Test with the payload as a suffix. + TestStrings[NumTestStrings++] = Prefix + PayloadDec; + TestStrings[NumTestStrings++] = Prefix + PayloadHex; + + // Test with the payload inside parentheses. + TestStrings[NumTestStrings++] = Prefix + '(' + PayloadDec + ')'; + TestStrings[NumTestStrings++] = Prefix + '(' + PayloadHex + ')'; + + for (size_t K = 0; K < NumTestStrings; ++K) { + StringRef TestStr = TestStrings[K]; + + APFloat F(Sem); + bool HasError = !F.convertFromString( + TestStr, llvm::APFloat::rmNearestTiesToEven); + EXPECT_FALSE(HasError); + EXPECT_TRUE(F.isNaN()); + EXPECT_EQ(Signaling, F.isSignaling()); + EXPECT_EQ(Negative, F.isNegative()); + uint64_t PayloadResult = + F.bitcastToAPInt().getZExtValue() & PayloadMask; + EXPECT_EQ(Payload, PayloadResult); + } + } + } + } + + const StringRef InfStrings[] = {"inf", "INFINITY", "+Inf", + "-inf", "-INFINITY", "-Inf"}; + for (StringRef InfStr : InfStrings) { + bool Negative = InfStr.front() == '-'; + + StringRef TestStr; + APFloat F(Sem); + bool HasError = + !F.convertFromString(InfStr, llvm::APFloat::rmNearestTiesToEven); + EXPECT_FALSE(HasError); + EXPECT_TRUE(F.isInfinity()); + EXPECT_EQ(Negative, F.isNegative()); + uint64_t PayloadResult = F.bitcastToAPInt().getZExtValue() & PayloadMask; + EXPECT_EQ(0, PayloadResult); + } +} + TEST(APFloatTest, fromToStringSpecials) { auto expects = [] (const char *first, const char *second) { std::string roundtrip = convertToString(convertToDoubleFromString(second), 0, 3);