fix parsing of bad binary exponents in hex floats (#4501)

- The binary exponent must have some decimal digits
- A + or - after the binary exponent digits should not be interpreted as
  part of the binary exponent.

Fixes: #4500
This commit is contained in:
David Neto 2021-09-03 12:27:12 -04:00 committed by GitHub
parent 789de0dc4b
commit 7e860e3831
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 85 additions and 1 deletions

View File

@ -1005,6 +1005,9 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
is.get();
next_char = is.peek();
}
// Finished reading the part preceding any '.' or 'p'.
bits_written = false;
while (seen_dot && !seen_p) {
// Handle only fractional parts now.
@ -1037,11 +1040,16 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
next_char = is.peek();
}
// Finished reading the part preceding 'p'.
// In hex floats syntax, the binary exponent is required.
bool seen_sign = false;
int8_t exponent_sign = 1;
bool seen_written_exponent_digits = false;
int_type written_exponent = 0;
while (true) {
if ((next_char == '-' || next_char == '+')) {
if (!seen_written_exponent_digits &&
(next_char == '-' || next_char == '+')) {
if (seen_sign) {
is.setstate(std::ios::failbit);
return is;
@ -1049,6 +1057,7 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
seen_sign = true;
exponent_sign = (next_char == '-') ? -1 : 1;
} else if (::isdigit(next_char)) {
seen_written_exponent_digits = true;
// Hex-floats express their exponent as decimal.
written_exponent = static_cast<int_type>(written_exponent * 10);
written_exponent =
@ -1059,6 +1068,11 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
is.get();
next_char = is.peek();
}
if (!seen_written_exponent_digits) {
// Binary exponent had no digits.
is.setstate(std::ios::failbit);
return is;
}
written_exponent = static_cast<int_type>(written_exponent * exponent_sign);
exponent = static_cast<int_type>(exponent + written_exponent);

View File

@ -1325,6 +1325,76 @@ TEST(FloatProxy, Lowest) {
Eq(std::numeric_limits<double>::lowest()));
}
template <typename T>
struct StreamParseCase {
StreamParseCase(const std::string& lit, bool succ, const std::string& suffix,
T value)
: literal(lit),
expect_success(succ),
expected_suffix(suffix),
expected_value(HexFloat<FloatProxy<T>>(value)) {}
std::string literal;
bool expect_success;
std::string expected_suffix;
HexFloat<FloatProxy<T>> expected_value;
};
template <typename T>
std::ostream& operator<<(std::ostream& os, const StreamParseCase<T>& fspc) {
os << "StreamParseCase(" << fspc.literal
<< ", expect_succes:" << int(fspc.expect_success) << ","
<< fspc.expected_suffix << "," << fspc.expected_value << ")";
return os;
}
using FloatStreamParseTest = ::testing::TestWithParam<StreamParseCase<float>>;
TEST_P(FloatStreamParseTest, Samples) {
std::stringstream input(GetParam().literal);
HexFloat<FloatProxy<float>> parsed_value(0.0f);
// Hex floats must be read with the stream input operator.
input >> parsed_value;
if (GetParam().expect_success) {
EXPECT_FALSE(input.fail());
std::string suffix;
input >> suffix;
// EXPECT_EQ(suffix, GetParam().expected_suffix);
EXPECT_EQ(parsed_value.value().getAsFloat(),
GetParam().expected_value.value().getAsFloat());
} else {
EXPECT_TRUE(input.fail());
}
}
INSTANTIATE_TEST_SUITE_P(
HexFloatExponentMissingDigits, FloatStreamParseTest,
::testing::ValuesIn(std::vector<StreamParseCase<float>>{
{"0x1.0p1", true, "", 2.0f},
{"0x1.0p1a", true, "a", 2.0f},
{"-0x1.0p1f", true, "f", -2.0f},
{"0x1.0p", false, "", 0.0f},
{"0x1.0pa", false, "", 0.0f},
{"0x1.0p!", false, "", 0.0f},
{"0x1.0p+", false, "", 0.0f},
{"0x1.0p+a", false, "", 0.0f},
{"0x1.0p+!", false, "", 0.0f},
{"0x1.0p-", false, "", 0.0f},
{"0x1.0p-a", false, "", 0.0f},
{"0x1.0p-!", false, "", 0.0f},
{"0x1.0p++", false, "", 0.0f},
{"0x1.0p+-", false, "", 0.0f},
{"0x1.0p-+", false, "", 0.0f},
{"0x1.0p--", false, "", 0.0f}}));
INSTANTIATE_TEST_SUITE_P(
HexFloatExponentTrailingSign, FloatStreamParseTest,
::testing::ValuesIn(std::vector<StreamParseCase<float>>{
// Don't consume a sign after the binary exponent digits.
{"0x1.0p1", true, "", 2.0f},
{"0x1.0p1+", true, "+", 2.0f},
{"0x1.0p1-", true, "-", 2.0f}}));
// TODO(awoloszyn): Add fp16 tests and HexFloatTraits.
} // namespace
} // namespace utils