From 6dfd4186f599d11e79870e043d4d1a57c2606ac8 Mon Sep 17 00:00:00 2001 From: Andrew Woloszyn Date: Mon, 2 Nov 2015 11:45:38 -0500 Subject: [PATCH] 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. --- include/util/bitutils.h | 14 +- include/util/hex_float.h | 95 +++++++++++-- test/HexFloat.cpp | 299 +++++++++++++++++++++++++++------------ 3 files changed, 298 insertions(+), 110 deletions(-) mode change 100644 => 100755 include/util/bitutils.h mode change 100644 => 100755 include/util/hex_float.h mode change 100644 => 100755 test/HexFloat.cpp diff --git a/include/util/bitutils.h b/include/util/bitutils.h old mode 100644 new mode 100755 index 473d31c65..3bb70f330 --- a/include/util/bitutils.h +++ b/include/util/bitutils.h @@ -44,13 +44,15 @@ Dest BitwiseCast(Src source) { // SetBits returns an integer of type with bits set // for position through , counting from the least // significant bit. In particular when Num == 0, no positions are set to 1. -template +template struct SetBits { - const static T get = (T(1) << First) | - SetBits::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::get; }; -template +template struct SetBits { const static T get = T(0); }; @@ -66,13 +68,11 @@ static_assert(SetBits::get == uint32_t(0x00000006), "SetBits failed"); static_assert(SetBits::get == uint32_t(0xc0000000), "SetBits failed"); -static_assert(SetBits::get == uint32_t(0x80000000), - "SetBits failed"); static_assert(SetBits::get == uint32_t(0x7FFFFFFF), "SetBits failed"); static_assert(SetBits::get == uint32_t(0xFFFFFFFF), "SetBits failed"); -static_assert(SetBits::get == uint32_t(0xFFFF0000), +static_assert(SetBits::get == uint32_t(0xFFFF0000), "SetBits failed"); static_assert(SetBits::get == uint64_t(0x0000000000000001LL), diff --git a/include/util/hex_float.h b/include/util/hex_float.h old mode 100644 new mode 100755 index d03a0f97a..23970482c --- a/include/util/hex_float.h +++ b/include/util/hex_float.h @@ -39,6 +39,75 @@ namespace spvutils { +template +struct FloatProxyTraits { + typedef void uint_type; +}; + +template <> +struct FloatProxyTraits { + typedef uint32_t uint_type; +}; + +template <> +struct FloatProxyTraits { + 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 +class FloatProxy { + public: + using uint_type = typename FloatProxyTraits::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(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 operator-() const { + return data_ ^ (uint_type(0x1) << (sizeof(T) * 8 - 1)); + } + + // Returns the data as a floating point value. + T getAsFloat() const { return BitwiseCast(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 +bool operator==(const FloatProxy& first, const FloatProxy& second) { + return first.data() == second.data(); +} + +// Convenience to read the value as a normal float. +template +std::istream& operator>>(std::istream& is, FloatProxy& value) { + T float_val; + is >> float_val; + value = FloatProxy(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 @@ -63,7 +132,7 @@ struct HexFloatTraits { // Traits for IEEE float. // 1 sign bit, 8 exponent bits, 23 fractional bits. template <> -struct HexFloatTraits { +struct HexFloatTraits> { typedef uint32_t uint_type; typedef int32_t int_type; static const uint_type num_used_bits = 32; @@ -75,7 +144,7 @@ struct HexFloatTraits { // Traits for IEEE double. // 1 sign bit, 11 exponent bits, 52 fractional bits. template <> -struct HexFloatTraits { +struct HexFloatTraits> { 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& value) { const uint_type bits = spvutils::BitwiseCast(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& value) { } template -inline std::istream& ParseNormalFloat( - std::istream& is, bool negate_value, HexFloat& value) { +inline std::istream& ParseNormalFloat(std::istream& is, bool negate_value, + HexFloat& value) { T val; is >> val; if (negate_value) { @@ -269,7 +340,7 @@ std::istream& operator>>(std::istream& is, HexFloat& 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& 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& 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& 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 diff --git a/test/HexFloat.cpp b/test/HexFloat.cpp old mode 100644 new mode 100755 index f80f9e1db..86f854fbb --- a/test/HexFloat.cpp +++ b/test/HexFloat.cpp @@ -28,141 +28,258 @@ #include "util/hex_float.h" #include -#include +#include #include #include - +#include namespace { using ::testing::Eq; using spvutils::BitwiseCast; +using spvutils::FloatProxy; using HexFloatTest = - ::testing::TestWithParam>; + ::testing::TestWithParam, std::string>>; using DecodeHexFloatTest = - ::testing::TestWithParam>; + ::testing::TestWithParam>>; TEST_P(HexFloatTest, EncodeCorrectly) { std::stringstream ss; - ss << spvutils::HexFloat(std::get<0>(GetParam())); + ss << spvutils::HexFloat>(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 myFloat(0.f); + spvutils::HexFloat> myFloat(0.f); ss >> myFloat; - float expected = std::get<0>(GetParam()); + FloatProxy 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(myFloat.value()), - Eq(BitwiseCast(std::get<0>(GetParam())))); + EXPECT_THAT(myFloat.value(), Eq(expected)); } INSTANTIATE_TEST_CASE_P( Float32Tests, HexFloatTest, - ::testing::ValuesIn(std::vector>({ - {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::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(0xFF800000), "-0x1p+128"}, // -inf - {BitwiseCast(0x7F800000), "0x1p+128"}, // inf - {BitwiseCast(0xFFC00000), "-0x1.8p+128"}, // -nan - {BitwiseCast(0xFF800100), "-0x1.0002p+128"}, // -nan - {BitwiseCast(0xFF800c00), "-0x1.0018p+128"}, // -nan - {BitwiseCast(0xFF80F000), "-0x1.01ep+128"}, // -nan - {BitwiseCast(0xFFFFFFFF), "-0x1.fffffep+128"}, // -nan - {BitwiseCast(0x7FC00000), "0x1.8p+128"}, // +nan - {BitwiseCast(0x7F800100), "0x1.0002p+128"}, // +nan - {BitwiseCast(0x7f800c00), "0x1.0018p+128"}, // +nan - {BitwiseCast(0x7F80F000), "0x1.01ep+128"}, // +nan - {BitwiseCast(0x7FFFFFFF), "0x1.fffffep+128"}, // +nan - }))); + }))); + +INSTANTIATE_TEST_CASE_P( + Float32NanTests, HexFloatTest, + ::testing::ValuesIn( + std::vector, 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 myFloat(0.f); + spvutils::HexFloat> myFloat(0.f); ss >> myFloat; - float expected = std::get<1>(GetParam()); + FloatProxy 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(myFloat.value()), - Eq(BitwiseCast(std::get<1>(GetParam())))); + Eq(BitwiseCast(expected))); } INSTANTIATE_TEST_CASE_P( Float32DecodeTests, DecodeHexFloatTest, - ::testing::ValuesIn(std::vector>({ - {"0x0p+000", 0.f}, - {"0x0p0", 0.f}, - {"0x0p-0", 0.f}, + ::testing::ValuesIn( + std::vector>>({ + {"0x0p+000", 0.f}, + {"0x0p0", 0.f}, + {"0x0p-0", 0.f}, - // inf cases - {"-0x1p+128", BitwiseCast(0xFF800000)}, // -inf - {"0x32p+127", BitwiseCast(0x7F800000)}, // inf - {"0x32p+500", BitwiseCast(0x7F800000)}, // inf - {"-0x32p+127", BitwiseCast(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>>({ + // 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(1.f).getAsFloat(), Eq(1.0f)); + EXPECT_THAT(FloatProxy(32.f).getAsFloat(), Eq(32.0f)); + EXPECT_THAT(FloatProxy(-1.f).getAsFloat(), Eq(-1.0f)); + EXPECT_THAT(FloatProxy(0.f).getAsFloat(), Eq(0.0f)); + EXPECT_THAT(FloatProxy(-0.f).getAsFloat(), Eq(-0.0f)); + EXPECT_THAT(FloatProxy(1.2e32f).getAsFloat(), Eq(1.2e32f)); + + EXPECT_TRUE( + std::isinf(FloatProxy(uint32_t(0xFF800000)).getAsFloat())); + EXPECT_TRUE( + std::isinf(FloatProxy(uint32_t(0x7F800000)).getAsFloat())); + EXPECT_TRUE( + std::isnan(FloatProxy(uint32_t(0xFFC00000)).getAsFloat())); + EXPECT_TRUE( + std::isnan(FloatProxy(uint32_t(0xFF800100)).getAsFloat())); + EXPECT_TRUE( + std::isnan(FloatProxy(uint32_t(0xFF800c00)).getAsFloat())); + EXPECT_TRUE( + std::isnan(FloatProxy(uint32_t(0xFF80F000)).getAsFloat())); + EXPECT_TRUE( + std::isnan(FloatProxy(uint32_t(0xFFFFFFFF)).getAsFloat())); + EXPECT_TRUE( + std::isnan(FloatProxy(uint32_t(0x7FC00000)).getAsFloat())); + EXPECT_TRUE( + std::isnan(FloatProxy(uint32_t(0x7F800100)).getAsFloat())); + EXPECT_TRUE( + std::isnan(FloatProxy(uint32_t(0x7f800c00)).getAsFloat())); + EXPECT_TRUE( + std::isnan(FloatProxy(uint32_t(0x7F80F000)).getAsFloat())); + EXPECT_TRUE( + std::isnan(FloatProxy(uint32_t(0x7FFFFFFF)).getAsFloat())); + + EXPECT_THAT(FloatProxy(uint32_t(0xFF800000)).data(), + Eq(0xFF800000)); + EXPECT_THAT(FloatProxy(uint32_t(0x7F800000)).data(), + Eq(0x7F800000)); + EXPECT_THAT(FloatProxy(uint32_t(0xFFC00000)).data(), + Eq(0xFFC00000)); + EXPECT_THAT(FloatProxy(uint32_t(0xFF800100)).data(), + Eq(0xFF800100)); + EXPECT_THAT(FloatProxy(uint32_t(0xFF800c00)).data(), + Eq(0xFF800c00)); + EXPECT_THAT(FloatProxy(uint32_t(0xFF80F000)).data(), + Eq(0xFF80F000)); + EXPECT_THAT(FloatProxy(uint32_t(0xFFFFFFFF)).data(), + Eq(0xFFFFFFFF)); + EXPECT_THAT(FloatProxy(uint32_t(0x7FC00000)).data(), + Eq(0x7FC00000)); + EXPECT_THAT(FloatProxy(uint32_t(0x7F800100)).data(), + Eq(0x7F800100)); + EXPECT_THAT(FloatProxy(uint32_t(0x7f800c00)).data(), + Eq(0x7f800c00)); + EXPECT_THAT(FloatProxy(uint32_t(0x7F80F000)).data(), + Eq(0x7F80F000)); + EXPECT_THAT(FloatProxy(uint32_t(0x7FFFFFFF)).data(), + Eq(0x7FFFFFFF)); +} + +TEST(FloatProxy, Nan) { + EXPECT_TRUE( + FloatProxy(uint32_t(0xFFC00000)).isNan()); + EXPECT_TRUE( + FloatProxy(uint32_t(0xFF800100)).isNan()); + EXPECT_TRUE( + FloatProxy(uint32_t(0xFF800c00)).isNan()); + EXPECT_TRUE( + FloatProxy(uint32_t(0xFF80F000)).isNan()); + EXPECT_TRUE( + FloatProxy(uint32_t(0xFFFFFFFF)).isNan()); + EXPECT_TRUE( + FloatProxy(uint32_t(0x7FC00000)).isNan()); + EXPECT_TRUE( + FloatProxy(uint32_t(0x7F800100)).isNan()); + EXPECT_TRUE( + FloatProxy(uint32_t(0x7f800c00)).isNan()); + EXPECT_TRUE( + FloatProxy(uint32_t(0x7F80F000)).isNan()); + EXPECT_TRUE( + FloatProxy(uint32_t(0x7FFFFFFF)).isNan()); +} + +TEST(FloatProxy, Negation) { + EXPECT_THAT((-FloatProxy(1.f)).getAsFloat(), Eq(-1.0f)); + EXPECT_THAT((-FloatProxy(0.f)).getAsFloat(), Eq(-0.0f)); + + EXPECT_THAT((-FloatProxy(-1.f)).getAsFloat(), Eq(1.0f)); + EXPECT_THAT((-FloatProxy(-0.f)).getAsFloat(), Eq(0.0f)); + + EXPECT_THAT((-FloatProxy(32.f)).getAsFloat(), Eq(-32.0f)); + EXPECT_THAT((-FloatProxy(-32.f)).getAsFloat(), Eq(32.0f)); + + EXPECT_THAT((-FloatProxy(1.2e32f)).getAsFloat(), Eq(-1.2e32f)); + EXPECT_THAT((-FloatProxy(-1.2e32f)).getAsFloat(), Eq(1.2e32f)); + + EXPECT_THAT( + (-FloatProxy(std::numeric_limits::infinity())).getAsFloat(), + Eq(-std::numeric_limits::infinity())); + EXPECT_THAT( + (-FloatProxy(-std::numeric_limits::infinity())).getAsFloat(), + Eq(std::numeric_limits::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. - }