mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-10-18 11:10:05 +00:00
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:
parent
789de0dc4b
commit
7e860e3831
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user