1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 11:02:59 +02:00

[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
This commit is contained in:
Ehud Katz 2020-01-21 20:17:42 +02:00
parent 091ee2007c
commit b85eaf5298
2 changed files with 169 additions and 9 deletions

View File

@ -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;

View File

@ -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);