diff options
-rw-r--r-- | vespalib/src/tests/util/brain_float16/brain_float16_test.cpp | 70 | ||||
-rw-r--r-- | vespalib/src/vespa/vespalib/util/brain_float16.h | 14 |
2 files changed, 71 insertions, 13 deletions
diff --git a/vespalib/src/tests/util/brain_float16/brain_float16_test.cpp b/vespalib/src/tests/util/brain_float16/brain_float16_test.cpp index 4b7b0ee7897..6de41b3e038 100644 --- a/vespalib/src/tests/util/brain_float16/brain_float16_test.cpp +++ b/vespalib/src/tests/util/brain_float16/brain_float16_test.cpp @@ -4,6 +4,8 @@ #include <vespa/vespalib/gtest/gtest.h> #include <stdio.h> #include <cmath> +#include <cmath> +#include <cfenv> using namespace vespalib; @@ -57,25 +59,73 @@ TEST(BrainFloat16Test, traits_check) { EXPECT_TRUE(std::has_unique_object_representations<BrainFloat16>::value); } +static std::string hexdump(const void *p, size_t sz) { + char tmpbuf[10]; + if (sz == 2) { + uint16_t bits; + memcpy(&bits, p, sz); + snprintf(tmpbuf, 10, "%04x", bits); + } else if (sz == 4) { + uint32_t bits; + memcpy(&bits, p, sz); + snprintf(tmpbuf, 10, "%08x", bits); + } else { + abort(); + } + return tmpbuf; +} +#define HEX_DUMP(arg) hexdump(&arg, sizeof(arg)).c_str() + TEST(BrainFloat16Test, check_special_values) { + // we should not need to support HW without normal float support: + EXPECT_TRUE(std::numeric_limits<float>::has_quiet_NaN); + EXPECT_TRUE(std::numeric_limits<float>::has_signaling_NaN); + EXPECT_TRUE(std::numeric_limits<BrainFloat16>::has_quiet_NaN); + EXPECT_TRUE(std::numeric_limits<BrainFloat16>::has_signaling_NaN); + std::feclearexcept(FE_ALL_EXCEPT); + EXPECT_TRUE(std::fetestexcept(FE_INVALID) == 0); float f_inf = std::numeric_limits<float>::infinity(); float f_neg = -f_inf; - float f_nan = std::numeric_limits<float>::quiet_NaN(); - BrainFloat16 b_inf = f_inf; - BrainFloat16 b_neg = f_neg; - BrainFloat16 b_nan = f_nan; + float f_qnan = std::numeric_limits<float>::quiet_NaN(); + float f_snan = std::numeric_limits<float>::signaling_NaN(); + BrainFloat16 b_inf = std::numeric_limits<BrainFloat16>::infinity(); + BrainFloat16 b_qnan = std::numeric_limits<BrainFloat16>::quiet_NaN(); + BrainFloat16 b_snan = std::numeric_limits<BrainFloat16>::signaling_NaN(); + BrainFloat16 b_from_f_inf = f_inf; + BrainFloat16 b_from_f_neg = f_neg; + BrainFloat16 b_from_f_qnan = f_qnan; + BrainFloat16 b_from_f_snan = f_snan; + EXPECT_EQ(sizeof(b_inf), 2); + EXPECT_EQ(memcmp(&b_inf, &b_from_f_inf, sizeof(BrainFloat16)), 0); + EXPECT_EQ(memcmp(&b_qnan, &b_from_f_qnan, sizeof(BrainFloat16)), 0); + EXPECT_EQ(memcmp(&b_snan, &b_from_f_snan, sizeof(BrainFloat16)), 0); + printf("+inf float is '%s' / bf16 is '%s'\n", HEX_DUMP(f_inf), HEX_DUMP(b_from_f_inf)); + printf("-inf float is '%s' / bf16 is '%s'\n", HEX_DUMP(f_neg), HEX_DUMP(b_from_f_neg)); + printf("qNaN float is '%s' / bf16 is '%s'\n", HEX_DUMP(f_qnan), HEX_DUMP(b_from_f_qnan)); + printf("sNan float is '%s' / bf16 is '%s'\n", HEX_DUMP(f_snan), HEX_DUMP(b_from_f_snan)); double d_inf = b_inf; - double d_neg = b_neg; - double d_nan = b_nan; + double d_neg = b_from_f_neg; + double d_qnan = b_qnan; + EXPECT_TRUE(std::fetestexcept(FE_INVALID) == 0); + // float->double conversion of signaling NaN: + double d_snan = b_snan; + EXPECT_TRUE(std::fetestexcept(FE_INVALID) != 0); + std::feclearexcept(FE_ALL_EXCEPT); + EXPECT_TRUE(std::fetestexcept(FE_INVALID) == 0); EXPECT_EQ(d_inf, std::numeric_limits<double>::infinity()); EXPECT_EQ(d_neg, -std::numeric_limits<double>::infinity()); - EXPECT_TRUE(std::isnan(d_nan)); + EXPECT_TRUE(std::isnan(d_qnan)); + EXPECT_TRUE(std::isnan(d_snan)); float f_from_b_inf = b_inf; - float f_from_b_neg = b_neg; - float f_from_b_nan = b_nan; + float f_from_b_neg = b_from_f_neg; + float f_from_b_qnan = b_qnan; + float f_from_b_snan = b_snan; EXPECT_EQ(memcmp(&f_inf, &f_from_b_inf, sizeof(float)), 0); EXPECT_EQ(memcmp(&f_neg, &f_from_b_neg, sizeof(float)), 0); - EXPECT_EQ(memcmp(&f_nan, &f_from_b_nan, sizeof(float)), 0); + EXPECT_EQ(memcmp(&f_qnan, &f_from_b_qnan, sizeof(float)), 0); + EXPECT_EQ(memcmp(&f_snan, &f_from_b_snan, sizeof(float)), 0); + // none of the BF16 operations should trigger FPE: + EXPECT_TRUE(std::fetestexcept(FE_INVALID) == 0); } GTEST_MAIN_RUN_ALL_TESTS() diff --git a/vespalib/src/vespa/vespalib/util/brain_float16.h b/vespalib/src/vespa/vespalib/util/brain_float16.h index 190f6be8450..85ac7dce107 100644 --- a/vespalib/src/vespa/vespalib/util/brain_float16.h +++ b/vespalib/src/vespa/vespalib/util/brain_float16.h @@ -69,8 +69,8 @@ public: static constexpr bool is_integer = false; static constexpr bool is_exact = false; static constexpr bool has_infinity = false; - static constexpr bool has_quiet_NaN = false; - static constexpr bool has_signaling_NaN = false; + static constexpr bool has_quiet_NaN = true; + static constexpr bool has_signaling_NaN = true; static constexpr bool has_denorm = true; static constexpr bool has_denorm_loss = false; static constexpr bool is_iec559 = false; @@ -98,7 +98,15 @@ public: static constexpr vespalib::BrainFloat16 max() noexcept { return 0x1.FEp127; } static constexpr vespalib::BrainFloat16 min() noexcept { return 0x1.0p-126; } static constexpr vespalib::BrainFloat16 round_error() noexcept { return 1.0; } - + static constexpr vespalib::BrainFloat16 infinity() noexcept { + return std::numeric_limits<float>::infinity(); + } + static constexpr vespalib::BrainFloat16 quiet_NaN() noexcept { + return std::numeric_limits<float>::quiet_NaN(); + } + static constexpr vespalib::BrainFloat16 signaling_NaN() noexcept { + return std::numeric_limits<float>::signaling_NaN(); + } }; } |