summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--vespalib/src/tests/util/brain_float16/brain_float16_test.cpp70
-rw-r--r--vespalib/src/vespa/vespalib/util/brain_float16.h14
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();
+ }
};
}