X-Git-Url: https://git.sesse.net/?p=movit;a=blobdiff_plain;f=fp16_test.cpp;h=7a9d56612b53dbe942d2b6d1b8a079b5cdfa6c3c;hp=d5d7fc04c22396a48fc7bbf73b239629f6fd39e6;hb=7f1fabb816ee5010be83abf5da58e92429609547;hpb=1f4a7aefd4a149e93a1dc41f85958e827670c98b diff --git a/fp16_test.cpp b/fp16_test.cpp index d5d7fc0..7a9d566 100644 --- a/fp16_test.cpp +++ b/fp16_test.cpp @@ -4,77 +4,92 @@ #include namespace movit { +namespace { + +fp16_int_t make_fp16(unsigned short x) +{ + fp16_int_t ret; + ret.val = x; + return ret; +} + +} // namespace TEST(FP16Test, Simple) { - EXPECT_EQ(0x0000, fp64_to_fp16(0.0)); - EXPECT_DOUBLE_EQ(0.0, fp16_to_fp64(0x0000)); + EXPECT_EQ(0x0000, fp32_to_fp16(0.0).val); + EXPECT_DOUBLE_EQ(0.0, fp16_to_fp32(make_fp16(0x0000))); - EXPECT_EQ(0x3c00, fp64_to_fp16(1.0)); - EXPECT_DOUBLE_EQ(1.0, fp16_to_fp64(0x3c00)); + EXPECT_EQ(0x3c00, fp32_to_fp16(1.0).val); + EXPECT_DOUBLE_EQ(1.0, fp16_to_fp32(make_fp16(0x3c00))); - EXPECT_EQ(0x3555, fp64_to_fp16(1.0 / 3.0)); - EXPECT_DOUBLE_EQ(0.333251953125, fp16_to_fp64(0x3555)); + EXPECT_EQ(0x3555, fp32_to_fp16(1.0 / 3.0).val); + EXPECT_DOUBLE_EQ(0.333251953125, fp16_to_fp32(make_fp16(0x3555))); } -TEST(FP16Test, NaN) { - EXPECT_EQ(0xfe00, fp64_to_fp16(0.0 / 0.0)); - EXPECT_TRUE(isnan(fp16_to_fp64(0xfe00))); +TEST(FP16Test, RoundToNearestEven) { + ASSERT_DOUBLE_EQ(1.0, fp16_to_fp32(make_fp16(0x3c00))); + + double x0 = fp16_to_fp32(make_fp16(0x3c00)); + double x1 = fp16_to_fp32(make_fp16(0x3c01)); + double x2 = fp16_to_fp32(make_fp16(0x3c02)); + double x3 = fp16_to_fp32(make_fp16(0x3c03)); + double x4 = fp16_to_fp32(make_fp16(0x3c04)); + + EXPECT_EQ(0x3c00, fp32_to_fp16(0.5 * (x0 + x1)).val); + EXPECT_EQ(0x3c02, fp32_to_fp16(0.5 * (x1 + x2)).val); + EXPECT_EQ(0x3c02, fp32_to_fp16(0.5 * (x2 + x3)).val); + EXPECT_EQ(0x3c04, fp32_to_fp16(0.5 * (x3 + x4)).val); } union fp64 { double f; unsigned long long ll; }; + +#ifdef __F16C__ union fp32 { float f; unsigned int u; }; +#endif + +TEST(FP16Test, NaN) { + // Ignore the sign bit. + EXPECT_EQ(0x7e00, fp32_to_fp16(0.0 / 0.0).val & 0x7fff); + EXPECT_TRUE(isnan(fp16_to_fp32(make_fp16(0xfe00)))); + + fp32 borderline_inf; + borderline_inf.u = 0x7f800000ull; + fp32 borderline_nan; + borderline_nan.u = 0x7f800001ull; + + ASSERT_FALSE(isfinite(borderline_inf.f)); + ASSERT_FALSE(isnan(borderline_inf.f)); + + ASSERT_FALSE(isfinite(borderline_nan.f)); + ASSERT_TRUE(isnan(borderline_nan.f)); + + double borderline_inf_roundtrip = fp16_to_fp32(fp32_to_fp16(borderline_inf.f)); + double borderline_nan_roundtrip = fp16_to_fp32(fp32_to_fp16(borderline_nan.f)); + + EXPECT_FALSE(isfinite(borderline_inf_roundtrip)); + EXPECT_FALSE(isnan(borderline_inf_roundtrip)); -// Randomly test a large number of fp64 -> fp32 conversions, comparing -// against the FPU. -TEST(FP16Test, FP32ReferenceDownconvert) { - srand(12345); - - for (int i = 0; i < 1000000; ++i) { - unsigned r1 = rand(); - unsigned r2 = rand(); - unsigned r3 = rand(); - union fp64 src; - union fp32 reference, result; - - src.ll = (((unsigned long long)r1) << 33) ^ ((unsigned long long)r2 << 16) ^ r3; - reference.f = float(src.f); - result.u = fp64_to_fp32(src.f); - - EXPECT_EQ(isnan(result.f), isnan(reference.f)); - if (!isnan(result.f)) { - EXPECT_EQ(result.u, reference.u) - << src.f << " got rounded to " << result.u << " (" << result.f << ")"; - } - } + EXPECT_FALSE(isfinite(borderline_nan_roundtrip)); + EXPECT_TRUE(isnan(borderline_nan_roundtrip)); } -// Randomly test a large number of fp32 -> fp64 conversions, comparing -// against the FPU. -TEST(FP16Test, FP32ReferenceUpconvert) { - srand(12345); - - for (int i = 0; i < 1000000; ++i) { - unsigned r1 = rand(); - unsigned r2 = rand(); - union fp32 src; - union fp64 reference, result; - - src.u = ((unsigned long long)r1 << 16) ^ r2; - reference.f = double(src.f); - result.f = fp32_to_fp64(src.u); - - EXPECT_EQ(isnan(result.f), isnan(reference.f)); - if (!isnan(result.f)) { - EXPECT_EQ(result.ll, reference.ll) - << src.f << " got converted to " << result.ll << " (" << result.f << ")"; - } - } +TEST(FP16Test, Denormals) { + const double smallest_fp16_denormal = 5.9604644775390625e-08; + EXPECT_EQ(0x0001, fp32_to_fp16(smallest_fp16_denormal).val); + EXPECT_EQ(0x0000, fp32_to_fp16(0.5 * smallest_fp16_denormal).val); // Round-to-even. + EXPECT_EQ(0x0001, fp32_to_fp16(0.51 * smallest_fp16_denormal).val); + EXPECT_EQ(0x0002, fp32_to_fp16(1.5 * smallest_fp16_denormal).val); + + const double smallest_fp16_non_denormal = 6.103515625e-05; + EXPECT_EQ(0x0400, fp32_to_fp16(smallest_fp16_non_denormal).val); + EXPECT_EQ(0x0400, fp32_to_fp16(smallest_fp16_non_denormal - 0.5 * smallest_fp16_denormal).val); // Round-to-even. + EXPECT_EQ(0x03ff, fp32_to_fp16(smallest_fp16_non_denormal - smallest_fp16_denormal).val); } } // namespace movit