Fix a bug where combined fp16 weights would be horribly wrong.
[movit] / fp16_test.cpp
1 #include "fp16.h"
2
3 #include <math.h>
4 #include <gtest/gtest.h>
5
6 namespace movit {
7 namespace {
8
9 fp16_int_t make_fp16(unsigned short x)
10 {
11         fp16_int_t ret;
12         ret.val = x;
13         return ret;
14 }
15
16 fp32_int_t make_fp32(unsigned int x)
17 {
18         fp32_int_t ret;
19         ret.val = x;
20         return ret;
21 }
22
23 }  // namespace
24
25 TEST(FP16Test, Simple) {
26         EXPECT_EQ(0x0000, fp64_to_fp16(0.0).val);
27         EXPECT_DOUBLE_EQ(0.0, fp16_to_fp64(make_fp16(0x0000)));
28
29         EXPECT_EQ(0x3c00, fp64_to_fp16(1.0).val);
30         EXPECT_DOUBLE_EQ(1.0, fp16_to_fp64(make_fp16(0x3c00)));
31
32         EXPECT_EQ(0x3555, fp64_to_fp16(1.0 / 3.0).val);
33         EXPECT_DOUBLE_EQ(0.333251953125, fp16_to_fp64(make_fp16(0x3555)));
34 }
35
36 TEST(FP16Test, RoundToNearestEven) {
37         ASSERT_DOUBLE_EQ(1.0, fp16_to_fp64(make_fp16(0x3c00)));
38
39         double x0 = fp16_to_fp64(make_fp16(0x3c00));
40         double x1 = fp16_to_fp64(make_fp16(0x3c01));
41         double x2 = fp16_to_fp64(make_fp16(0x3c02));
42         double x3 = fp16_to_fp64(make_fp16(0x3c03));
43         double x4 = fp16_to_fp64(make_fp16(0x3c04));
44
45         EXPECT_EQ(0x3c00, fp64_to_fp16(0.5 * (x0 + x1)).val);
46         EXPECT_EQ(0x3c02, fp64_to_fp16(0.5 * (x1 + x2)).val);
47         EXPECT_EQ(0x3c02, fp64_to_fp16(0.5 * (x2 + x3)).val);
48         EXPECT_EQ(0x3c04, fp64_to_fp16(0.5 * (x3 + x4)).val);
49 }
50
51 union fp64 {
52         double f;
53         unsigned long long ll;
54 };
55 union fp32 {
56         float f;
57         unsigned int u;
58 };
59
60 TEST(FP16Test, NaN) {
61         // Ignore the sign bit.
62         EXPECT_EQ(0x7e00, fp64_to_fp16(0.0 / 0.0).val & 0x7fff);
63         EXPECT_TRUE(isnan(fp16_to_fp64(make_fp16(0xfe00))));
64
65         fp64 borderline_inf;
66         borderline_inf.ll = 0x7ff0000000000000ull;
67         fp64 borderline_nan;
68         borderline_nan.ll = 0x7ff0000000000001ull;
69
70         ASSERT_FALSE(isfinite(borderline_inf.f));
71         ASSERT_FALSE(isnan(borderline_inf.f));
72
73         ASSERT_FALSE(isfinite(borderline_nan.f));
74         ASSERT_TRUE(isnan(borderline_nan.f));
75
76         double borderline_inf_roundtrip = fp16_to_fp64(fp64_to_fp16(borderline_inf.f));
77         double borderline_nan_roundtrip = fp16_to_fp64(fp64_to_fp16(borderline_nan.f));
78
79         EXPECT_FALSE(isfinite(borderline_inf_roundtrip));
80         EXPECT_FALSE(isnan(borderline_inf_roundtrip));
81
82         EXPECT_FALSE(isfinite(borderline_nan_roundtrip));
83         EXPECT_TRUE(isnan(borderline_nan_roundtrip));
84 }
85
86 TEST(FP16Test, Denormals) {
87         const double smallest_fp16_denormal = 5.9604644775390625e-08;
88         EXPECT_EQ(0x0001, fp64_to_fp16(smallest_fp16_denormal).val);
89         EXPECT_EQ(0x0000, fp64_to_fp16(0.5 * smallest_fp16_denormal).val);  // Round-to-even.
90         EXPECT_EQ(0x0001, fp64_to_fp16(0.51 * smallest_fp16_denormal).val);
91         EXPECT_EQ(0x0002, fp64_to_fp16(1.5 * smallest_fp16_denormal).val);
92
93         const double smallest_fp16_non_denormal = 6.103515625e-05;
94         EXPECT_EQ(0x0400, fp64_to_fp16(smallest_fp16_non_denormal).val);
95         EXPECT_EQ(0x0400, fp64_to_fp16(smallest_fp16_non_denormal - 0.5 * smallest_fp16_denormal).val);  // Round-to-even.
96         EXPECT_EQ(0x03ff, fp64_to_fp16(smallest_fp16_non_denormal - smallest_fp16_denormal).val);
97 }
98
99 // Randomly test a large number of fp64 -> fp32 conversions, comparing
100 // against the FPU.
101 TEST(FP16Test, FP32ReferenceDownconvert) {
102         srand(12345);
103
104         for (int i = 0; i < 1000000; ++i) {
105                 unsigned r1 = rand();
106                 unsigned r2 = rand();
107                 unsigned r3 = rand();
108                 union fp64 src;
109                 union fp32 reference, result;
110
111                 src.ll = (((unsigned long long)r1) << 33) ^ ((unsigned long long)r2 << 16) ^ r3;
112                 reference.f = float(src.f);
113                 result.u = fp64_to_fp32(src.f).val;
114
115                 EXPECT_EQ(isnan(result.f), isnan(reference.f));
116                 if (!isnan(result.f)) {
117                         EXPECT_EQ(result.u, reference.u)
118                             << src.f << " got rounded to " << result.u << " (" << result.f << ")";
119                 }
120         }
121 }
122
123 // Randomly test a large number of fp32 -> fp64 conversions, comparing
124 // against the FPU.
125 TEST(FP16Test, FP32ReferenceUpconvert) {
126         srand(12345);
127
128         for (int i = 0; i < 1000000; ++i) {
129                 unsigned r1 = rand();
130                 unsigned r2 = rand();
131                 union fp32 src;
132                 union fp64 reference, result;
133
134                 src.u = ((unsigned long long)r1 << 16) ^ r2;
135                 reference.f = double(src.f);
136                 result.f = fp32_to_fp64(make_fp32(src.u));
137
138                 EXPECT_EQ(isnan(result.f), isnan(reference.f));
139                 if (!isnan(result.f)) {
140                         EXPECT_EQ(result.ll, reference.ll)
141                             << src.f << " got converted to " << result.ll << " (" << result.f << ")";
142                 }
143         }
144 }
145
146 }  // namespace movit