mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-23 04:00:05 +00:00
Updated HexFloat parsing for windows.
It is valid for float values to be modified on copy if they are NaN, so long as they remain the correct NaN. What this means is that we can not rely on the float data-type for storing float values if we want to retain bit patterns. Added FloatProxy which stores data in an unsigned integer, and updated the HexFloat template to deal with FloatProxy values instead.
This commit is contained in:
parent
0b61c593a2
commit
6dfd4186f5
14
include/util/bitutils.h
Normal file → Executable file
14
include/util/bitutils.h
Normal file → Executable file
@ -44,13 +44,15 @@ Dest BitwiseCast(Src source) {
|
||||
// SetBits<T, First, Num> returns an integer of type <T> with bits set
|
||||
// for position <First> through <First + Num - 1>, counting from the least
|
||||
// significant bit. In particular when Num == 0, no positions are set to 1.
|
||||
template<typename T, size_t First = 0, size_t Num = 0>
|
||||
template <typename T, size_t First = 0, size_t Num = 0>
|
||||
struct SetBits {
|
||||
const static T get = (T(1) << First) |
|
||||
SetBits<T, First+1, Num-1>::get;
|
||||
static_assert(First < sizeof(T) * 8,
|
||||
"Tried to set a bit that is shifted too far.");
|
||||
const static T get =
|
||||
(T(1) << First) | SetBits<T, First + 1, Num - 1>::get;
|
||||
};
|
||||
|
||||
template<typename T, size_t Last>
|
||||
template <typename T, size_t Last>
|
||||
struct SetBits<T, Last, 0> {
|
||||
const static T get = T(0);
|
||||
};
|
||||
@ -66,13 +68,11 @@ static_assert(SetBits<uint32_t, 1, 2>::get == uint32_t(0x00000006),
|
||||
"SetBits failed");
|
||||
static_assert(SetBits<uint32_t, 30, 2>::get == uint32_t(0xc0000000),
|
||||
"SetBits failed");
|
||||
static_assert(SetBits<uint32_t, 31, 3>::get == uint32_t(0x80000000),
|
||||
"SetBits failed");
|
||||
static_assert(SetBits<uint32_t, 0, 31>::get == uint32_t(0x7FFFFFFF),
|
||||
"SetBits failed");
|
||||
static_assert(SetBits<uint32_t, 0, 32>::get == uint32_t(0xFFFFFFFF),
|
||||
"SetBits failed");
|
||||
static_assert(SetBits<uint32_t, 16, 31>::get == uint32_t(0xFFFF0000),
|
||||
static_assert(SetBits<uint32_t, 16, 16>::get == uint32_t(0xFFFF0000),
|
||||
"SetBits failed");
|
||||
|
||||
static_assert(SetBits<uint64_t, 0, 1>::get == uint64_t(0x0000000000000001LL),
|
||||
|
95
include/util/hex_float.h
Normal file → Executable file
95
include/util/hex_float.h
Normal file → Executable file
@ -39,6 +39,75 @@
|
||||
|
||||
namespace spvutils {
|
||||
|
||||
template <typename T>
|
||||
struct FloatProxyTraits {
|
||||
typedef void uint_type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct FloatProxyTraits<float> {
|
||||
typedef uint32_t uint_type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct FloatProxyTraits<double> {
|
||||
typedef uint64_t uint_type;
|
||||
};
|
||||
|
||||
// Since copying a floating point number (especially if it is NaN)
|
||||
// does not guarantee that bits are preserved, this class lets us
|
||||
// store the type and use it as a float when necessary.
|
||||
template <typename T>
|
||||
class FloatProxy {
|
||||
public:
|
||||
using uint_type = typename FloatProxyTraits<T>::uint_type;
|
||||
|
||||
// Since this is to act similar to the normal floats,
|
||||
// do not initialize the data by default.
|
||||
FloatProxy() = default;
|
||||
|
||||
// Intentionally non-explicit. This is a proxy type so
|
||||
// implicit conversions allow us to use it more transparently.
|
||||
FloatProxy(T val) {
|
||||
data_ = BitwiseCast<uint_type>(val);
|
||||
}
|
||||
|
||||
// Intentionally non-explicit. This is a proxy type so
|
||||
// implicit conversions allow us to use it more transparently.
|
||||
FloatProxy(uint_type val) { data_ = val; }
|
||||
|
||||
// This is helpful to have and is guaranteed not to stomp bits.
|
||||
FloatProxy<T> operator-() const {
|
||||
return data_ ^ (uint_type(0x1) << (sizeof(T) * 8 - 1));
|
||||
}
|
||||
|
||||
// Returns the data as a floating point value.
|
||||
T getAsFloat() const { return BitwiseCast<T>(data_); }
|
||||
|
||||
// Returns the raw data.
|
||||
uint_type data() const { return data_; }
|
||||
|
||||
// Returns true if the value represents any type of NaN.
|
||||
bool isNan() { return std::isnan(getAsFloat()); }
|
||||
|
||||
private:
|
||||
uint_type data_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
bool operator==(const FloatProxy<T>& first, const FloatProxy<T>& second) {
|
||||
return first.data() == second.data();
|
||||
}
|
||||
|
||||
// Convenience to read the value as a normal float.
|
||||
template <typename T>
|
||||
std::istream& operator>>(std::istream& is, FloatProxy<T>& value) {
|
||||
T float_val;
|
||||
is >> float_val;
|
||||
value = FloatProxy<T>(float_val);
|
||||
return is;
|
||||
}
|
||||
|
||||
// This is an example traits. It is not meant to be used in practice, but will
|
||||
// be the default for any non-specialized type.
|
||||
template <typename T>
|
||||
@ -63,7 +132,7 @@ struct HexFloatTraits {
|
||||
// Traits for IEEE float.
|
||||
// 1 sign bit, 8 exponent bits, 23 fractional bits.
|
||||
template <>
|
||||
struct HexFloatTraits<float> {
|
||||
struct HexFloatTraits<FloatProxy<float>> {
|
||||
typedef uint32_t uint_type;
|
||||
typedef int32_t int_type;
|
||||
static const uint_type num_used_bits = 32;
|
||||
@ -75,7 +144,7 @@ struct HexFloatTraits<float> {
|
||||
// Traits for IEEE double.
|
||||
// 1 sign bit, 11 exponent bits, 52 fractional bits.
|
||||
template <>
|
||||
struct HexFloatTraits<double> {
|
||||
struct HexFloatTraits<FloatProxy<double>> {
|
||||
typedef uint64_t uint_type;
|
||||
typedef int64_t int_type;
|
||||
static const uint_type num_used_bits = 64;
|
||||
@ -94,6 +163,7 @@ class HexFloat {
|
||||
using int_type = typename Traits::int_type;
|
||||
|
||||
explicit HexFloat(T f) : value_(f) {}
|
||||
|
||||
T value() const { return value_; }
|
||||
void set_value(T f) { value_ = f; }
|
||||
|
||||
@ -114,7 +184,7 @@ class HexFloat {
|
||||
// then we have to left-shift to get rid of leading 0s. This is the amount
|
||||
// we have to shift (might be 0).
|
||||
static const uint32_t num_overflow_bits =
|
||||
fraction_nibbles * 4 - num_fraction_bits;
|
||||
fraction_nibbles * 4 - num_fraction_bits;
|
||||
|
||||
// The representation of the fraction, not the actual bits. This
|
||||
// includes the leading bit that is usually implicit.
|
||||
@ -123,7 +193,7 @@ class HexFloat {
|
||||
|
||||
// The topmost bit in the fraction. (The first non-implicit bit).
|
||||
static const uint_type fraction_top_bit =
|
||||
uint_type(1) << num_fraction_bits + num_overflow_bits - 1;
|
||||
uint_type(1) << (num_fraction_bits + num_overflow_bits - 1);
|
||||
|
||||
// The mask for the encoded fraction. It does not include the
|
||||
// implicit bit.
|
||||
@ -142,7 +212,7 @@ class HexFloat {
|
||||
|
||||
// How far from the right edge the fraction is shifted.
|
||||
static const uint32_t fraction_right_shift =
|
||||
(sizeof(uint_type)*8) - num_fraction_bits;
|
||||
(sizeof(uint_type) * 8) - num_fraction_bits;
|
||||
|
||||
private:
|
||||
T value_;
|
||||
@ -183,7 +253,8 @@ std::ostream& operator<<(std::ostream& os, const HexFloat<T, Traits>& value) {
|
||||
|
||||
const uint_type bits = spvutils::BitwiseCast<uint_type>(value.value());
|
||||
const char* const sign = (bits & HF::sign_mask) ? "-" : "";
|
||||
const uint_type exponent = (bits & HF::exponent_mask) >> HF::num_fraction_bits;
|
||||
const uint_type exponent =
|
||||
(bits & HF::exponent_mask) >> HF::num_fraction_bits;
|
||||
|
||||
uint_type fraction = (bits & HF::fraction_encode_mask)
|
||||
<< HF::num_overflow_bits;
|
||||
@ -233,8 +304,8 @@ std::ostream& operator<<(std::ostream& os, const HexFloat<T, Traits>& value) {
|
||||
}
|
||||
|
||||
template <typename T, typename Traits>
|
||||
inline std::istream& ParseNormalFloat(
|
||||
std::istream& is, bool negate_value, HexFloat<T, Traits>& value) {
|
||||
inline std::istream& ParseNormalFloat(std::istream& is, bool negate_value,
|
||||
HexFloat<T, Traits>& value) {
|
||||
T val;
|
||||
is >> val;
|
||||
if (negate_value) {
|
||||
@ -269,7 +340,7 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
|
||||
using uint_type = typename HF::uint_type;
|
||||
using int_type = typename HF::int_type;
|
||||
|
||||
value.set_value(T(0));
|
||||
value.set_value(T(0.f));
|
||||
|
||||
if (is.flags() & std::ios::skipws) {
|
||||
// If the user wants to skip whitespace , then we should obey that.
|
||||
@ -282,7 +353,7 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
|
||||
bool negate_value = false;
|
||||
|
||||
if (next_char != '-' && next_char != '0') {
|
||||
return ParseNormalFloat(is, negate_value, value);
|
||||
return ParseNormalFloat(is, negate_value, value);
|
||||
}
|
||||
|
||||
if (next_char == '-') {
|
||||
@ -341,7 +412,7 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
|
||||
fraction |= write_bit << (HF::top_bit_left_shift - fraction_index++);
|
||||
exponent += 1;
|
||||
}
|
||||
bits_written |= write_bit;
|
||||
bits_written |= write_bit != 0;
|
||||
}
|
||||
} else {
|
||||
// We have not found our exponent yet, so we have to fail.
|
||||
@ -360,7 +431,7 @@ std::istream& operator>>(std::istream& is, HexFloat<T, Traits>& value) {
|
||||
int number = get_nibble_from_character(next_char);
|
||||
for (int i = 0; i < 4; ++i, number <<= 1) {
|
||||
uint_type write_bit = (number & 0x8) ? 0x01 : 0x00;
|
||||
bits_written |= write_bit;
|
||||
bits_written |= write_bit != 0;
|
||||
if (is_denorm && !bits_written) {
|
||||
// Handle modifying the exponent here this way we can handle
|
||||
// an arbitrary number of hex values without overflowing our
|
||||
|
299
test/HexFloat.cpp
Normal file → Executable file
299
test/HexFloat.cpp
Normal file → Executable file
@ -28,141 +28,258 @@
|
||||
#include "util/hex_float.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <tuple>
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace {
|
||||
using ::testing::Eq;
|
||||
using spvutils::BitwiseCast;
|
||||
using spvutils::FloatProxy;
|
||||
|
||||
using HexFloatTest =
|
||||
::testing::TestWithParam<std::pair<float, std::string>>;
|
||||
::testing::TestWithParam<std::pair<FloatProxy<float>, std::string>>;
|
||||
using DecodeHexFloatTest =
|
||||
::testing::TestWithParam<std::pair<std::string, float>>;
|
||||
::testing::TestWithParam<std::pair<std::string, FloatProxy<float>>>;
|
||||
|
||||
TEST_P(HexFloatTest, EncodeCorrectly) {
|
||||
std::stringstream ss;
|
||||
ss << spvutils::HexFloat<float>(std::get<0>(GetParam()));
|
||||
ss << spvutils::HexFloat<FloatProxy<float>>(std::get<0>(GetParam()));
|
||||
EXPECT_THAT(ss.str(), Eq(std::get<1>(GetParam())));
|
||||
}
|
||||
|
||||
TEST_P(HexFloatTest, DecodeCorrectly) {
|
||||
std::stringstream ss(std::get<1>(GetParam()));
|
||||
spvutils::HexFloat<float> myFloat(0.f);
|
||||
spvutils::HexFloat<FloatProxy<float>> myFloat(0.f);
|
||||
ss >> myFloat;
|
||||
float expected = std::get<0>(GetParam());
|
||||
FloatProxy<float> expected = std::get<0>(GetParam());
|
||||
// Check that the two floats are bitwise equal. We do it this way because
|
||||
// nan != nan, and so the test would fail even if they were exactly the same.
|
||||
EXPECT_THAT(BitwiseCast<uint32_t>(myFloat.value()),
|
||||
Eq(BitwiseCast<uint32_t>(std::get<0>(GetParam()))));
|
||||
EXPECT_THAT(myFloat.value(), Eq(expected));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
Float32Tests, HexFloatTest,
|
||||
::testing::ValuesIn(std::vector<std::pair<float, std::string>>({
|
||||
{0.f, "0x0p+0"},
|
||||
{1.f, "0x1p+0"},
|
||||
{2.f, "0x1p+1"},
|
||||
{3.f, "0x1.8p+1"},
|
||||
{0.5f, "0x1p-1"},
|
||||
{0.25f, "0x1p-2"},
|
||||
{0.75f, "0x1.8p-1"},
|
||||
{-0.f, "-0x0p+0"},
|
||||
{-1.f, "-0x1p+0"},
|
||||
{-0.5f, "-0x1p-1"},
|
||||
{-0.25f, "-0x1p-2"},
|
||||
{-0.75f, "-0x1.8p-1"},
|
||||
::testing::ValuesIn(
|
||||
std::vector<std::pair<FloatProxy<float>, std::string>>({
|
||||
{0.f, "0x0p+0"},
|
||||
{1.f, "0x1p+0"},
|
||||
{2.f, "0x1p+1"},
|
||||
{3.f, "0x1.8p+1"},
|
||||
{0.5f, "0x1p-1"},
|
||||
{0.25f, "0x1p-2"},
|
||||
{0.75f, "0x1.8p-1"},
|
||||
{-0.f, "-0x0p+0"},
|
||||
{-1.f, "-0x1p+0"},
|
||||
{-0.5f, "-0x1p-1"},
|
||||
{-0.25f, "-0x1p-2"},
|
||||
{-0.75f, "-0x1.8p-1"},
|
||||
|
||||
// Larger numbers
|
||||
{512.f, "0x1p+9"},
|
||||
{-512.f, "-0x1p+9"},
|
||||
{1024.f, "0x1p+10"},
|
||||
{-1024.f, "-0x1p+10"},
|
||||
{1024.f + 8.f, "0x1.02p+10"},
|
||||
{-1024.f - 8.f, "-0x1.02p+10"},
|
||||
// Larger numbers
|
||||
{512.f, "0x1p+9"},
|
||||
{-512.f, "-0x1p+9"},
|
||||
{1024.f, "0x1p+10"},
|
||||
{-1024.f, "-0x1p+10"},
|
||||
{1024.f + 8.f, "0x1.02p+10"},
|
||||
{-1024.f - 8.f, "-0x1.02p+10"},
|
||||
|
||||
// Small numbers
|
||||
{1.0f / 512.f, "0x1p-9"},
|
||||
{1.0f / -512.f, "-0x1p-9"},
|
||||
{1.0f / 1024.f, "0x1p-10"},
|
||||
{1.0f / -1024.f, "-0x1p-10"},
|
||||
{1.0f / 1024.f + 1.0f / 8.f, "0x1.02p-3"},
|
||||
{1.0f / -1024.f - 1.0f / 8.f, "-0x1.02p-3"},
|
||||
// Small numbers
|
||||
{1.0f / 512.f, "0x1p-9"},
|
||||
{1.0f / -512.f, "-0x1p-9"},
|
||||
{1.0f / 1024.f, "0x1p-10"},
|
||||
{1.0f / -1024.f, "-0x1p-10"},
|
||||
{1.0f / 1024.f + 1.0f / 8.f, "0x1.02p-3"},
|
||||
{1.0f / -1024.f - 1.0f / 8.f, "-0x1.02p-3"},
|
||||
|
||||
// lowest non-denorm
|
||||
{ldexp(1.0f, -126), "0x1p-126"},
|
||||
{ldexp(-1.0f, -126), "-0x1p-126"},
|
||||
// lowest non-denorm
|
||||
{float(ldexp(1.0f, -126)), "0x1p-126"},
|
||||
{float(ldexp(-1.0f, -126)), "-0x1p-126"},
|
||||
|
||||
// Denormalized values
|
||||
{ldexp(1.0f, -127), "0x1p-127"},
|
||||
{ldexp(1.0f, -127) / 2.0f, "0x1p-128"},
|
||||
{ldexp(1.0f, -127) / 4.0f, "0x1p-129"},
|
||||
{ldexp(1.0f, -127) / 8.0f, "0x1p-130"},
|
||||
{ldexp(-1.0f, -127), "-0x1p-127"},
|
||||
{ldexp(-1.0f, -127) / 2.0f, "-0x1p-128"},
|
||||
{ldexp(-1.0f, -127) / 4.0f, "-0x1p-129"},
|
||||
{ldexp(-1.0f, -127) / 8.0f, "-0x1p-130"},
|
||||
// Denormalized values
|
||||
{float(ldexp(1.0f, -127)), "0x1p-127"},
|
||||
{float(ldexp(1.0f, -127) / 2.0f), "0x1p-128"},
|
||||
{float(ldexp(1.0f, -127) / 4.0f), "0x1p-129"},
|
||||
{float(ldexp(1.0f, -127) / 8.0f), "0x1p-130"},
|
||||
{float(ldexp(-1.0f, -127)), "-0x1p-127"},
|
||||
{float(ldexp(-1.0f, -127) / 2.0f), "-0x1p-128"},
|
||||
{float(ldexp(-1.0f, -127) / 4.0f), "-0x1p-129"},
|
||||
{float(ldexp(-1.0f, -127) / 8.0f), "-0x1p-130"},
|
||||
|
||||
{ldexp(1.0, -127) + (ldexp(1.0, -127) / 2.0f), "0x1.8p-127"},
|
||||
{ldexp(1.0, -127) / 2.0 + (ldexp(1.0, -127) / 4.0f), "0x1.8p-128"},
|
||||
{float(ldexp(1.0, -127) + (ldexp(1.0, -127) / 2.0f)), "0x1.8p-127"},
|
||||
{float(ldexp(1.0, -127) / 2.0 + (ldexp(1.0, -127) / 4.0f)),
|
||||
"0x1.8p-128"},
|
||||
|
||||
// Various NAN and INF cases
|
||||
{BitwiseCast<float>(0xFF800000), "-0x1p+128"}, // -inf
|
||||
{BitwiseCast<float>(0x7F800000), "0x1p+128"}, // inf
|
||||
{BitwiseCast<float>(0xFFC00000), "-0x1.8p+128"}, // -nan
|
||||
{BitwiseCast<float>(0xFF800100), "-0x1.0002p+128"}, // -nan
|
||||
{BitwiseCast<float>(0xFF800c00), "-0x1.0018p+128"}, // -nan
|
||||
{BitwiseCast<float>(0xFF80F000), "-0x1.01ep+128"}, // -nan
|
||||
{BitwiseCast<float>(0xFFFFFFFF), "-0x1.fffffep+128"}, // -nan
|
||||
{BitwiseCast<float>(0x7FC00000), "0x1.8p+128"}, // +nan
|
||||
{BitwiseCast<float>(0x7F800100), "0x1.0002p+128"}, // +nan
|
||||
{BitwiseCast<float>(0x7f800c00), "0x1.0018p+128"}, // +nan
|
||||
{BitwiseCast<float>(0x7F80F000), "0x1.01ep+128"}, // +nan
|
||||
{BitwiseCast<float>(0x7FFFFFFF), "0x1.fffffep+128"}, // +nan
|
||||
})));
|
||||
})));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
Float32NanTests, HexFloatTest,
|
||||
::testing::ValuesIn(
|
||||
std::vector<std::pair<FloatProxy<float>, std::string>>({
|
||||
// Various NAN and INF cases
|
||||
{uint32_t(0xFF800000), "-0x1p+128"}, // -inf
|
||||
{uint32_t(0x7F800000), "0x1p+128"}, // inf
|
||||
{uint32_t(0xFFC00000), "-0x1.8p+128"}, // -nan
|
||||
{uint32_t(0xFF800100), "-0x1.0002p+128"}, // -nan
|
||||
{uint32_t(0xFF800c00), "-0x1.0018p+128"}, // -nan
|
||||
{uint32_t(0xFF80F000), "-0x1.01ep+128"}, // -nan
|
||||
{uint32_t(0xFFFFFFFF), "-0x1.fffffep+128"}, // -nan
|
||||
{uint32_t(0x7FC00000), "0x1.8p+128"}, // +nan
|
||||
{uint32_t(0x7F800100), "0x1.0002p+128"}, // +nan
|
||||
{uint32_t(0x7f800c00), "0x1.0018p+128"}, // +nan
|
||||
{uint32_t(0x7F80F000), "0x1.01ep+128"}, // +nan
|
||||
{uint32_t(0x7FFFFFFF), "0x1.fffffep+128"}, // +nan
|
||||
})));
|
||||
|
||||
TEST_P(DecodeHexFloatTest, DecodeCorrectly) {
|
||||
std::stringstream ss(std::get<0>(GetParam()));
|
||||
spvutils::HexFloat<float> myFloat(0.f);
|
||||
spvutils::HexFloat<FloatProxy<float>> myFloat(0.f);
|
||||
ss >> myFloat;
|
||||
float expected = std::get<1>(GetParam());
|
||||
FloatProxy<float> expected = std::get<1>(GetParam());
|
||||
// Check that the two floats are bitwise equal. We do it this way because
|
||||
// nan != nan, and so the test would fail even if they were exactly the same.
|
||||
EXPECT_THAT(BitwiseCast<uint32_t>(myFloat.value()),
|
||||
Eq(BitwiseCast<uint32_t>(std::get<1>(GetParam()))));
|
||||
Eq(BitwiseCast<uint32_t>(expected)));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
Float32DecodeTests, DecodeHexFloatTest,
|
||||
::testing::ValuesIn(std::vector<std::pair<std::string, float>>({
|
||||
{"0x0p+000", 0.f},
|
||||
{"0x0p0", 0.f},
|
||||
{"0x0p-0", 0.f},
|
||||
::testing::ValuesIn(
|
||||
std::vector<std::pair<std::string, FloatProxy<float>>>({
|
||||
{"0x0p+000", 0.f},
|
||||
{"0x0p0", 0.f},
|
||||
{"0x0p-0", 0.f},
|
||||
|
||||
// inf cases
|
||||
{"-0x1p+128", BitwiseCast<float>(0xFF800000)}, // -inf
|
||||
{"0x32p+127", BitwiseCast<float>(0x7F800000)}, // inf
|
||||
{"0x32p+500", BitwiseCast<float>(0x7F800000)}, // inf
|
||||
{"-0x32p+127", BitwiseCast<float>(0xFF800000)}, // -inf
|
||||
// flush to zero cases
|
||||
{"0x1p-500", 0.f}, // Exponent underflows.
|
||||
{"-0x1p-500", -0.f},
|
||||
{"0x0.00000000001p-126", 0.f}, // Fraction causes underflow.
|
||||
{"-0x0.0000000001p-127", -0.f},
|
||||
{"-0x0.01p-142",
|
||||
-0.f}, // Fraction causes undeflow to underflow more.
|
||||
{"0x0.01p-142", 0.f},
|
||||
|
||||
// flush to zero cases
|
||||
{"0x1p-500", 0.f}, // Exponent underflows.
|
||||
{"-0x1p-500", -0.f},
|
||||
{"0x0.00000000001p-126", 0.f}, // Fraction causes underfloat.
|
||||
{"-0x0.0000000001p-127", -0.f},
|
||||
{"-0x0.01p-142", -0.f}, // Fraction causes undeflow to underflow more.
|
||||
{"0x0.01p-142", 0.f},
|
||||
// Some floats that do not encode the same way as the decode.
|
||||
{"0x2p+0", 2.f},
|
||||
{"0xFFp+0", 255.f},
|
||||
{"0x0.8p+0", 0.5f},
|
||||
{"0x0.4p+0", 0.25f},
|
||||
})));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
Float32DecodeInfTests, DecodeHexFloatTest,
|
||||
::testing::ValuesIn(
|
||||
std::vector<std::pair<std::string, FloatProxy<float>>>({
|
||||
// inf cases
|
||||
{"-0x1p+128", uint32_t(0xFF800000)}, // -inf
|
||||
{"0x32p+127", uint32_t(0x7F800000)}, // inf
|
||||
{"0x32p+500", uint32_t(0x7F800000)}, // inf
|
||||
{"-0x32p+127", uint32_t(0xFF800000)}, // -inf
|
||||
})));
|
||||
|
||||
TEST(FloatProxy, ValidConversion) {
|
||||
EXPECT_THAT(FloatProxy<float>(1.f).getAsFloat(), Eq(1.0f));
|
||||
EXPECT_THAT(FloatProxy<float>(32.f).getAsFloat(), Eq(32.0f));
|
||||
EXPECT_THAT(FloatProxy<float>(-1.f).getAsFloat(), Eq(-1.0f));
|
||||
EXPECT_THAT(FloatProxy<float>(0.f).getAsFloat(), Eq(0.0f));
|
||||
EXPECT_THAT(FloatProxy<float>(-0.f).getAsFloat(), Eq(-0.0f));
|
||||
EXPECT_THAT(FloatProxy<float>(1.2e32f).getAsFloat(), Eq(1.2e32f));
|
||||
|
||||
EXPECT_TRUE(
|
||||
std::isinf(FloatProxy<float>(uint32_t(0xFF800000)).getAsFloat()));
|
||||
EXPECT_TRUE(
|
||||
std::isinf(FloatProxy<float>(uint32_t(0x7F800000)).getAsFloat()));
|
||||
EXPECT_TRUE(
|
||||
std::isnan(FloatProxy<float>(uint32_t(0xFFC00000)).getAsFloat()));
|
||||
EXPECT_TRUE(
|
||||
std::isnan(FloatProxy<float>(uint32_t(0xFF800100)).getAsFloat()));
|
||||
EXPECT_TRUE(
|
||||
std::isnan(FloatProxy<float>(uint32_t(0xFF800c00)).getAsFloat()));
|
||||
EXPECT_TRUE(
|
||||
std::isnan(FloatProxy<float>(uint32_t(0xFF80F000)).getAsFloat()));
|
||||
EXPECT_TRUE(
|
||||
std::isnan(FloatProxy<float>(uint32_t(0xFFFFFFFF)).getAsFloat()));
|
||||
EXPECT_TRUE(
|
||||
std::isnan(FloatProxy<float>(uint32_t(0x7FC00000)).getAsFloat()));
|
||||
EXPECT_TRUE(
|
||||
std::isnan(FloatProxy<float>(uint32_t(0x7F800100)).getAsFloat()));
|
||||
EXPECT_TRUE(
|
||||
std::isnan(FloatProxy<float>(uint32_t(0x7f800c00)).getAsFloat()));
|
||||
EXPECT_TRUE(
|
||||
std::isnan(FloatProxy<float>(uint32_t(0x7F80F000)).getAsFloat()));
|
||||
EXPECT_TRUE(
|
||||
std::isnan(FloatProxy<float>(uint32_t(0x7FFFFFFF)).getAsFloat()));
|
||||
|
||||
EXPECT_THAT(FloatProxy<float>(uint32_t(0xFF800000)).data(),
|
||||
Eq(0xFF800000));
|
||||
EXPECT_THAT(FloatProxy<float>(uint32_t(0x7F800000)).data(),
|
||||
Eq(0x7F800000));
|
||||
EXPECT_THAT(FloatProxy<float>(uint32_t(0xFFC00000)).data(),
|
||||
Eq(0xFFC00000));
|
||||
EXPECT_THAT(FloatProxy<float>(uint32_t(0xFF800100)).data(),
|
||||
Eq(0xFF800100));
|
||||
EXPECT_THAT(FloatProxy<float>(uint32_t(0xFF800c00)).data(),
|
||||
Eq(0xFF800c00));
|
||||
EXPECT_THAT(FloatProxy<float>(uint32_t(0xFF80F000)).data(),
|
||||
Eq(0xFF80F000));
|
||||
EXPECT_THAT(FloatProxy<float>(uint32_t(0xFFFFFFFF)).data(),
|
||||
Eq(0xFFFFFFFF));
|
||||
EXPECT_THAT(FloatProxy<float>(uint32_t(0x7FC00000)).data(),
|
||||
Eq(0x7FC00000));
|
||||
EXPECT_THAT(FloatProxy<float>(uint32_t(0x7F800100)).data(),
|
||||
Eq(0x7F800100));
|
||||
EXPECT_THAT(FloatProxy<float>(uint32_t(0x7f800c00)).data(),
|
||||
Eq(0x7f800c00));
|
||||
EXPECT_THAT(FloatProxy<float>(uint32_t(0x7F80F000)).data(),
|
||||
Eq(0x7F80F000));
|
||||
EXPECT_THAT(FloatProxy<float>(uint32_t(0x7FFFFFFF)).data(),
|
||||
Eq(0x7FFFFFFF));
|
||||
}
|
||||
|
||||
TEST(FloatProxy, Nan) {
|
||||
EXPECT_TRUE(
|
||||
FloatProxy<float>(uint32_t(0xFFC00000)).isNan());
|
||||
EXPECT_TRUE(
|
||||
FloatProxy<float>(uint32_t(0xFF800100)).isNan());
|
||||
EXPECT_TRUE(
|
||||
FloatProxy<float>(uint32_t(0xFF800c00)).isNan());
|
||||
EXPECT_TRUE(
|
||||
FloatProxy<float>(uint32_t(0xFF80F000)).isNan());
|
||||
EXPECT_TRUE(
|
||||
FloatProxy<float>(uint32_t(0xFFFFFFFF)).isNan());
|
||||
EXPECT_TRUE(
|
||||
FloatProxy<float>(uint32_t(0x7FC00000)).isNan());
|
||||
EXPECT_TRUE(
|
||||
FloatProxy<float>(uint32_t(0x7F800100)).isNan());
|
||||
EXPECT_TRUE(
|
||||
FloatProxy<float>(uint32_t(0x7f800c00)).isNan());
|
||||
EXPECT_TRUE(
|
||||
FloatProxy<float>(uint32_t(0x7F80F000)).isNan());
|
||||
EXPECT_TRUE(
|
||||
FloatProxy<float>(uint32_t(0x7FFFFFFF)).isNan());
|
||||
}
|
||||
|
||||
TEST(FloatProxy, Negation) {
|
||||
EXPECT_THAT((-FloatProxy<float>(1.f)).getAsFloat(), Eq(-1.0f));
|
||||
EXPECT_THAT((-FloatProxy<float>(0.f)).getAsFloat(), Eq(-0.0f));
|
||||
|
||||
EXPECT_THAT((-FloatProxy<float>(-1.f)).getAsFloat(), Eq(1.0f));
|
||||
EXPECT_THAT((-FloatProxy<float>(-0.f)).getAsFloat(), Eq(0.0f));
|
||||
|
||||
EXPECT_THAT((-FloatProxy<float>(32.f)).getAsFloat(), Eq(-32.0f));
|
||||
EXPECT_THAT((-FloatProxy<float>(-32.f)).getAsFloat(), Eq(32.0f));
|
||||
|
||||
EXPECT_THAT((-FloatProxy<float>(1.2e32f)).getAsFloat(), Eq(-1.2e32f));
|
||||
EXPECT_THAT((-FloatProxy<float>(-1.2e32f)).getAsFloat(), Eq(1.2e32f));
|
||||
|
||||
EXPECT_THAT(
|
||||
(-FloatProxy<float>(std::numeric_limits<float>::infinity())).getAsFloat(),
|
||||
Eq(-std::numeric_limits<float>::infinity()));
|
||||
EXPECT_THAT(
|
||||
(-FloatProxy<float>(-std::numeric_limits<float>::infinity())).getAsFloat(),
|
||||
Eq(std::numeric_limits<float>::infinity()));
|
||||
}
|
||||
|
||||
// Some floats that do not encode the same way as the decode.
|
||||
{"0x2p+0", 2.f},
|
||||
{"0xFFp+0", 255.f},
|
||||
{"0x0.8p+0", 0.5f},
|
||||
{"0x0.4p+0", 0.25f},
|
||||
})));
|
||||
// TODO(awoloszyn): Add some more decoding tests with "abnormal" formats.
|
||||
// TODO(awoloszyn): Add double tests
|
||||
// TODO(awoloszyn): Add fp16 tests and HexFloatTraits.
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user