[llvm] 68122b5 - [APFloat] Extend conversion from special strings
Ehud Katz via llvm-commits
llvm-commits at lists.llvm.org
Tue Jan 21 10:22:50 PST 2020
Author: Ehud Katz
Date: 2020-01-21T20:22:27+02:00
New Revision: 68122b5826b56f547e8fbae7cf4d455afeda8400
URL: https://github.com/llvm/llvm-project/commit/68122b5826b56f547e8fbae7cf4d455afeda8400
DIFF: https://github.com/llvm/llvm-project/commit/68122b5826b56f547e8fbae7cf4d455afeda8400.diff
LOG: [APFloat] Extend conversion from special strings
Add support for converting Signaling NaN, and a NaN Payload from string.
The NaNs (the string "nan" or "NaN") may be prefixed with 's' or 'S' for defining a Signaling NaN.
A payload for a NaN can be specified as a suffix.
It may be a octal/decimal/hexadecimal number in parentheses or without.
Differential Revision: https://reviews.llvm.org/D69773
Added:
Modified:
llvm/lib/Support/APFloat.cpp
llvm/unittests/ADT/APFloatTest.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp
index 050c37baefb8..37392e9f9df4 100644
--- a/llvm/lib/Support/APFloat.cpp
+++ b/llvm/lib/Support/APFloat.cpp
@@ -2621,24 +2621,70 @@ IEEEFloat::convertFromDecimalString(StringRef str, roundingMode rounding_mode) {
}
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
index 65b831c96e8f..fa12b5f69f6b 100644
--- a/llvm/unittests/ADT/APFloatTest.cpp
+++ b/llvm/unittests/ADT/APFloatTest.cpp
@@ -919,6 +919,120 @@ TEST(APFloatTest, fromDecimalString) {
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);
More information about the llvm-commits
mailing list