• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <gtest/gtest.h>
2 
3 #include <cstdint>
4 
5 #include <fp16.h>
6 #include "tables.h"
7 
8 #if (defined(__i386__) || defined(__x86_64__)) && defined(__F16C__)
9 	#include <x86intrin.h>
10 #endif
11 
12 
TEST(FP16_IEEE_FROM_FP32_VALUE,normalized_powers_of_2)13 TEST(FP16_IEEE_FROM_FP32_VALUE, normalized_powers_of_2) {
14 	const uint16_t min_po2_f16   = UINT16_C(0x0400);
15 	const uint16_t eighths_f16   = UINT16_C(0x3000);
16 	const uint16_t quarter_f16   = UINT16_C(0x3400);
17 	const uint16_t half_f16      = UINT16_C(0x3800);
18 	const uint16_t one_f16       = UINT16_C(0x3C00);
19 	const uint16_t two_f16       = UINT16_C(0x4000);
20 	const uint16_t four_f16      = UINT16_C(0x4400);
21 	const uint16_t eight_f16     = UINT16_C(0x4800);
22 	const uint16_t sixteen_f16   = UINT16_C(0x4C00);
23 	const uint16_t thirtytwo_f16 = UINT16_C(0x5000);
24 	const uint16_t sixtyfour_f16 = UINT16_C(0x5400);
25 	const uint16_t max_po2_f16   = UINT16_C(0x7800);
26 
27 	const uint32_t min_po2_f32   = UINT32_C(0x38800000);
28 	const uint32_t eighths_f32   = UINT32_C(0x3E000000);
29 	const uint32_t quarter_f32   = UINT32_C(0x3E800000);
30 	const uint32_t half_f32      = UINT32_C(0x3F000000);
31 	const uint32_t one_f32       = UINT32_C(0x3F800000);
32 	const uint32_t two_f32       = UINT32_C(0x40000000);
33 	const uint32_t four_f32      = UINT32_C(0x40800000);
34 	const uint32_t eight_f32     = UINT32_C(0x41000000);
35 	const uint32_t sixteen_f32   = UINT32_C(0x41800000);
36 	const uint32_t thirtytwo_f32 = UINT32_C(0x42000000);
37 	const uint32_t sixtyfour_f32 = UINT32_C(0x42800000);
38 	const uint32_t max_po2_f32   = UINT32_C(0x47000000);
39 
40 	float min_po2_value;
41 	memcpy(&min_po2_value, &min_po2_f32, sizeof(min_po2_value));
42 	EXPECT_EQ(min_po2_f16, fp16_ieee_from_fp32_value(min_po2_value)) <<
43 		std::hex << std::uppercase << std::setfill('0') <<
44 		"F32 = 0x" << std::setw(8) << min_po2_f32 << ", " <<
45 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(min_po2_value) << ", " <<
46 		"F16 = 0x" << std::setw(4) << min_po2_f16;
47 
48 	float eighths_value;
49 	memcpy(&eighths_value, &eighths_f32, sizeof(eighths_value));
50 	EXPECT_EQ(eighths_f16, fp16_ieee_from_fp32_value(eighths_value)) <<
51 		std::hex << std::uppercase << std::setfill('0') <<
52 		"F32 = 0x" << std::setw(8) << eighths_f32 << ", " <<
53 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(eighths_value) << ", " <<
54 		"F16 = 0x" << std::setw(4) << eighths_f16;
55 
56 	float quarter_value;
57 	memcpy(&quarter_value, &quarter_f32, sizeof(quarter_value));
58 	EXPECT_EQ(quarter_f16, fp16_ieee_from_fp32_value(quarter_value)) <<
59 		std::hex << std::uppercase << std::setfill('0') <<
60 		"F32 = 0x" << std::setw(8) << quarter_f32 << ", " <<
61 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(quarter_value) << ", " <<
62 		"F16 = 0x" << std::setw(4) << quarter_f16;
63 
64 	float half_value;
65 	memcpy(&half_value, &half_f32, sizeof(half_value));
66 	EXPECT_EQ(half_f16, fp16_ieee_from_fp32_value(half_value)) <<
67 		std::hex << std::uppercase << std::setfill('0') <<
68 		"F32 = 0x" << std::setw(8) << half_f32 << ", " <<
69 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(half_value) << ", " <<
70 		"F16 = 0x" << std::setw(4) << half_f16;
71 
72 	float one_value;
73 	memcpy(&one_value, &one_f32, sizeof(one_value));
74 	EXPECT_EQ(one_f16, fp16_ieee_from_fp32_value(one_value)) <<
75 		std::hex << std::uppercase << std::setfill('0') <<
76 		"F32 = 0x" << std::setw(8) << one_f32 << ", " <<
77 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(one_value) << ", " <<
78 		"F16 = 0x" << std::setw(4) << one_f16;
79 
80 	float two_value;
81 	memcpy(&two_value, &two_f32, sizeof(two_value));
82 	EXPECT_EQ(two_f16, fp16_ieee_from_fp32_value(two_value)) <<
83 		std::hex << std::uppercase << std::setfill('0') <<
84 		"F32 = 0x" << std::setw(8) << two_f32 << ", " <<
85 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(two_value) << ", " <<
86 		"F16 = 0x" << std::setw(4) << two_f16;
87 
88 	float four_value;
89 	memcpy(&four_value, &four_f32, sizeof(four_value));
90 	EXPECT_EQ(four_f16, fp16_ieee_from_fp32_value(four_value)) <<
91 		std::hex << std::uppercase << std::setfill('0') <<
92 		"F32 = 0x" << std::setw(8) << four_f32 << ", " <<
93 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(four_value) << ", " <<
94 		"F16 = 0x" << std::setw(4) << four_f16;
95 
96 	float eight_value;
97 	memcpy(&eight_value, &eight_f32, sizeof(eight_value));
98 	EXPECT_EQ(eight_f16, fp16_ieee_from_fp32_value(eight_value)) <<
99 		std::hex << std::uppercase << std::setfill('0') <<
100 		"F32 = 0x" << std::setw(8) << eight_f32 << ", " <<
101 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(eight_value) << ", " <<
102 		"F16 = 0x" << std::setw(4) << eight_f16;
103 
104 	float sixteen_value;
105 	memcpy(&sixteen_value, &sixteen_f32, sizeof(sixteen_value));
106 	EXPECT_EQ(sixteen_f16, fp16_ieee_from_fp32_value(sixteen_value)) <<
107 		std::hex << std::uppercase << std::setfill('0') <<
108 		"F32 = 0x" << std::setw(8) << sixteen_f32 << ", " <<
109 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(sixteen_value) << ", " <<
110 		"F16 = 0x" << std::setw(4) << sixteen_f16;
111 
112 	float thirtytwo_value;
113 	memcpy(&thirtytwo_value, &thirtytwo_f32, sizeof(thirtytwo_value));
114 	EXPECT_EQ(thirtytwo_f16, fp16_ieee_from_fp32_value(thirtytwo_value)) <<
115 		std::hex << std::uppercase << std::setfill('0') <<
116 		"F32 = 0x" << std::setw(8) << thirtytwo_f32 << ", " <<
117 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(thirtytwo_value) << ", " <<
118 		"F16 = 0x" << std::setw(4) << thirtytwo_f16;
119 
120 	float sixtyfour_value;
121 	memcpy(&sixtyfour_value, &sixtyfour_f32, sizeof(sixtyfour_value));
122 	EXPECT_EQ(sixtyfour_f16, fp16_ieee_from_fp32_value(sixtyfour_value)) <<
123 		std::hex << std::uppercase << std::setfill('0') <<
124 		"F32 = 0x" << std::setw(8) << sixtyfour_f32 << ", " <<
125 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(sixtyfour_value) << ", " <<
126 		"F16 = 0x" << std::setw(4) << sixtyfour_f16;
127 
128 	float max_po2_value;
129 	memcpy(&max_po2_value, &max_po2_f32, sizeof(max_po2_value));
130 	EXPECT_EQ(max_po2_f16, fp16_ieee_from_fp32_value(max_po2_value)) <<
131 		std::hex << std::uppercase << std::setfill('0') <<
132 		"F32 = 0x" << std::setw(8) << max_po2_f32 << ", " <<
133 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(max_po2_value) << ", " <<
134 		"F16 = 0x" << std::setw(4) << max_po2_f16;
135 }
136 
TEST(FP16_IEEE_FROM_FP32_VALUE,denormalized_powers_of_2)137 TEST(FP16_IEEE_FROM_FP32_VALUE, denormalized_powers_of_2) {
138 	const uint16_t exp2_minus_15_f16 = UINT16_C(0x0200);
139 	const uint16_t exp2_minus_16_f16 = UINT16_C(0x0100);
140 	const uint16_t exp2_minus_17_f16 = UINT16_C(0x0080);
141 	const uint16_t exp2_minus_18_f16 = UINT16_C(0x0040);
142 	const uint16_t exp2_minus_19_f16 = UINT16_C(0x0020);
143 	const uint16_t exp2_minus_20_f16 = UINT16_C(0x0010);
144 	const uint16_t exp2_minus_21_f16 = UINT16_C(0x0008);
145 	const uint16_t exp2_minus_22_f16 = UINT16_C(0x0004);
146 	const uint16_t exp2_minus_23_f16 = UINT16_C(0x0002);
147 	const uint16_t exp2_minus_24_f16 = UINT16_C(0x0001);
148 	const uint16_t exp2_minus_25_f16 = UINT16_C(0x0000);
149 
150 	const uint32_t exp2_minus_15_f32 = UINT32_C(0x38000000);
151 	const uint32_t exp2_minus_16_f32 = UINT32_C(0x37800000);
152 	const uint32_t exp2_minus_17_f32 = UINT32_C(0x37000000);
153 	const uint32_t exp2_minus_18_f32 = UINT32_C(0x36800000);
154 	const uint32_t exp2_minus_19_f32 = UINT32_C(0x36000000);
155 	const uint32_t exp2_minus_20_f32 = UINT32_C(0x35800000);
156 	const uint32_t exp2_minus_21_f32 = UINT32_C(0x35000000);
157 	const uint32_t exp2_minus_22_f32 = UINT32_C(0x34800000);
158 	const uint32_t exp2_minus_23_f32 = UINT32_C(0x34000000);
159 	const uint32_t exp2_minus_24_f32 = UINT32_C(0x33800000);
160 	const uint32_t exp2_minus_25_f32 = UINT32_C(0x33000000);
161 
162 	float exp2_minus_15_value;
163 	memcpy(&exp2_minus_15_value, &exp2_minus_15_f32, sizeof(exp2_minus_15_value));
164 	EXPECT_EQ(exp2_minus_15_f16, fp16_ieee_from_fp32_value(exp2_minus_15_value)) <<
165 		std::hex << std::uppercase << std::setfill('0') <<
166 		"F32 = 0x" << std::setw(8) << exp2_minus_15_f32 << ", " <<
167 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_15_value) << ", " <<
168 		"F16 = 0x" << std::setw(4) << exp2_minus_15_f16;
169 
170 	float exp2_minus_16_value;
171 	memcpy(&exp2_minus_16_value, &exp2_minus_16_f32, sizeof(exp2_minus_16_value));
172 	EXPECT_EQ(exp2_minus_16_f16, fp16_ieee_from_fp32_value(exp2_minus_16_value)) <<
173 		std::hex << std::uppercase << std::setfill('0') <<
174 		"F32 = 0x" << std::setw(8) << exp2_minus_16_f32 << ", " <<
175 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_16_value) << ", " <<
176 		"F16 = 0x" << std::setw(4) << exp2_minus_16_f16;
177 
178 	float exp2_minus_17_value;
179 	memcpy(&exp2_minus_17_value, &exp2_minus_17_f32, sizeof(exp2_minus_17_value));
180 	EXPECT_EQ(exp2_minus_17_f16, fp16_ieee_from_fp32_value(exp2_minus_17_value)) <<
181 		std::hex << std::uppercase << std::setfill('0') <<
182 		"F32 = 0x" << std::setw(8) << exp2_minus_17_f32 << ", " <<
183 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_17_value) << ", " <<
184 		"F16 = 0x" << std::setw(4) << exp2_minus_17_f16;
185 
186 	float exp2_minus_18_value;
187 	memcpy(&exp2_minus_18_value, &exp2_minus_18_f32, sizeof(exp2_minus_18_value));
188 	EXPECT_EQ(exp2_minus_18_f16, fp16_ieee_from_fp32_value(exp2_minus_18_value)) <<
189 		std::hex << std::uppercase << std::setfill('0') <<
190 		"F32 = 0x" << std::setw(8) << exp2_minus_18_f32 << ", " <<
191 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_18_value) << ", " <<
192 		"F16 = 0x" << std::setw(4) << exp2_minus_18_f16;
193 
194 	float exp2_minus_19_value;
195 	memcpy(&exp2_minus_19_value, &exp2_minus_19_f32, sizeof(exp2_minus_19_value));
196 	EXPECT_EQ(exp2_minus_19_f16, fp16_ieee_from_fp32_value(exp2_minus_19_value)) <<
197 		std::hex << std::uppercase << std::setfill('0') <<
198 		"F32 = 0x" << std::setw(8) << exp2_minus_19_f32 << ", " <<
199 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_19_value) << ", " <<
200 		"F16 = 0x" << std::setw(4) << exp2_minus_19_f16;
201 
202 	float exp2_minus_20_value;
203 	memcpy(&exp2_minus_20_value, &exp2_minus_20_f32, sizeof(exp2_minus_20_value));
204 	EXPECT_EQ(exp2_minus_20_f16, fp16_ieee_from_fp32_value(exp2_minus_20_value)) <<
205 		std::hex << std::uppercase << std::setfill('0') <<
206 		"F32 = 0x" << std::setw(8) << exp2_minus_20_f32 << ", " <<
207 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_20_value) << ", " <<
208 		"F16 = 0x" << std::setw(4) << exp2_minus_20_f16;
209 
210 	float exp2_minus_21_value;
211 	memcpy(&exp2_minus_21_value, &exp2_minus_21_f32, sizeof(exp2_minus_21_value));
212 	EXPECT_EQ(exp2_minus_21_f16, fp16_ieee_from_fp32_value(exp2_minus_21_value)) <<
213 		std::hex << std::uppercase << std::setfill('0') <<
214 		"F32 = 0x" << std::setw(8) << exp2_minus_21_f32 << ", " <<
215 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_21_value) << ", " <<
216 		"F16 = 0x" << std::setw(4) << exp2_minus_21_f16;
217 
218 	float exp2_minus_22_value;
219 	memcpy(&exp2_minus_22_value, &exp2_minus_22_f32, sizeof(exp2_minus_22_value));
220 	EXPECT_EQ(exp2_minus_22_f16, fp16_ieee_from_fp32_value(exp2_minus_22_value)) <<
221 		std::hex << std::uppercase << std::setfill('0') <<
222 		"F32 = 0x" << std::setw(8) << exp2_minus_22_f32 << ", " <<
223 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_22_value) << ", " <<
224 		"F16 = 0x" << std::setw(4) << exp2_minus_22_f16;
225 
226 	float exp2_minus_23_value;
227 	memcpy(&exp2_minus_23_value, &exp2_minus_23_f32, sizeof(exp2_minus_23_value));
228 	EXPECT_EQ(exp2_minus_23_f16, fp16_ieee_from_fp32_value(exp2_minus_23_value)) <<
229 		std::hex << std::uppercase << std::setfill('0') <<
230 		"F32 = 0x" << std::setw(8) << exp2_minus_23_f32 << ", " <<
231 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_23_value) << ", " <<
232 		"F16 = 0x" << std::setw(4) << exp2_minus_23_f16;
233 
234 	float exp2_minus_24_value;
235 	memcpy(&exp2_minus_24_value, &exp2_minus_24_f32, sizeof(exp2_minus_24_value));
236 	EXPECT_EQ(exp2_minus_24_f16, fp16_ieee_from_fp32_value(exp2_minus_24_value)) <<
237 		std::hex << std::uppercase << std::setfill('0') <<
238 		"F32 = 0x" << std::setw(8) << exp2_minus_24_f32 << ", " <<
239 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_24_value) << ", " <<
240 		"F16 = 0x" << std::setw(4) << exp2_minus_24_f16;
241 
242 	float exp2_minus_25_value;
243 	memcpy(&exp2_minus_25_value, &exp2_minus_25_f32, sizeof(exp2_minus_25_value));
244 	EXPECT_EQ(exp2_minus_25_f16, fp16_ieee_from_fp32_value(exp2_minus_25_value)) <<
245 		std::hex << std::uppercase << std::setfill('0') <<
246 		"F32 = 0x" << std::setw(8) << exp2_minus_25_f32 << ", " <<
247 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_25_value) << ", " <<
248 		"F16 = 0x" << std::setw(4) << exp2_minus_25_f16;
249 }
250 
TEST(FP16_IEEE_FROM_FP32_VALUE,zero)251 TEST(FP16_IEEE_FROM_FP32_VALUE, zero) {
252 	const uint16_t positive_zero_f16 = UINT16_C(0x0000);
253 	const uint16_t negative_zero_f16 = UINT16_C(0x8000);
254 
255 	const uint32_t positive_zero_f32 = UINT32_C(0x00000000);
256 	const uint32_t negative_zero_f32 = UINT32_C(0x80000000);
257 
258 	float positive_zero_value;
259 	memcpy(&positive_zero_value, &positive_zero_f32, sizeof(positive_zero_value));
260 	EXPECT_EQ(positive_zero_f16, fp16_ieee_from_fp32_value(positive_zero_value)) <<
261 		std::hex << std::uppercase << std::setfill('0') <<
262 		"F32 = 0x" << std::setw(8) << positive_zero_f32 << ", " <<
263 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(positive_zero_value) << ", " <<
264 		"F16 = 0x" << std::setw(4) << positive_zero_f16;
265 
266 	float negative_zero_value;
267 	memcpy(&negative_zero_value, &negative_zero_f32, sizeof(negative_zero_value));
268 	EXPECT_EQ(negative_zero_f16, fp16_ieee_from_fp32_value(negative_zero_value)) <<
269 		std::hex << std::uppercase << std::setfill('0') <<
270 		"F32 = 0x" << std::setw(8) << negative_zero_f32 << ", " <<
271 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(negative_zero_value) << ", " <<
272 		"F16 = 0x" << std::setw(4) << negative_zero_f16;
273 }
274 
TEST(FP16_IEEE_FROM_FP32_VALUE,infinity)275 TEST(FP16_IEEE_FROM_FP32_VALUE, infinity) {
276 	const uint16_t positive_infinity_f16 = UINT16_C(0x7C00);
277 	const uint16_t negative_infinity_f16 = UINT16_C(0xFC00);
278 
279 	const uint32_t positive_infinity_f32 = UINT32_C(0x7F800000);
280 	const uint32_t negative_infinity_f32 = UINT32_C(0xFF800000);
281 
282 	float positive_infinity_value;
283 	memcpy(&positive_infinity_value, &positive_infinity_f32, sizeof(positive_infinity_value));
284 	EXPECT_EQ(positive_infinity_f16, fp16_ieee_from_fp32_value(positive_infinity_value)) <<
285 		std::hex << std::uppercase << std::setfill('0') <<
286 		"F32 = 0x" << std::setw(8) << positive_infinity_f32 << ", " <<
287 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(positive_infinity_value) << ", " <<
288 		"F16 = 0x" << std::setw(4) << positive_infinity_f16;
289 
290 	float negative_infinity_value;
291 	memcpy(&negative_infinity_value, &negative_infinity_f32, sizeof(negative_infinity_value));
292 	EXPECT_EQ(negative_infinity_f16, fp16_ieee_from_fp32_value(negative_infinity_value)) <<
293 		std::hex << std::uppercase << std::setfill('0') <<
294 		"F32 = 0x" << std::setw(8) << negative_infinity_f32 << ", " <<
295 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(negative_infinity_value) << ", " <<
296 		"F16 = 0x" << std::setw(4) << negative_infinity_f16;
297 }
298 
TEST(FP16_IEEE_FROM_FP32_VALUE,positive_nan)299 TEST(FP16_IEEE_FROM_FP32_VALUE, positive_nan) {
300 	for (uint32_t nan_f32 = UINT32_C(0x7FFFFFFF); nan_f32 > UINT32_C(0x7F800000); nan_f32--) {
301 		float nan_value;
302 		memcpy(&nan_value, &nan_f32, sizeof(nan_value));
303 		const uint16_t nan_f16 = fp16_ieee_from_fp32_value(nan_value);
304 
305 		/* Check sign */
306 		ASSERT_EQ(nan_f16 & UINT16_C(0x8000), 0) <<
307 			std::hex << std::uppercase << std::setfill('0') <<
308 			"F32 = 0x" << std::setw(8) << nan_f32 << ", " <<
309 			"F16(F32) = 0x" << std::setw(4) << nan_f16;
310 
311 		/* Check exponent */
312 		ASSERT_EQ(nan_f16 & UINT16_C(0x7C00), UINT16_C(0x7C00)) <<
313 			std::hex << std::uppercase << std::setfill('0') <<
314 			"F32 = 0x" << std::setw(8) << nan_f32 << ", " <<
315 			"F16(F32) = 0x" << std::setw(4) << nan_f16;
316 
317 		/* Check mantissa */
318 		ASSERT_NE(nan_f16 & UINT16_C(0x03FF), 0) <<
319 			std::hex << std::uppercase << std::setfill('0') <<
320 			"F32 = 0x" << std::setw(8) << nan_f32 << ", " <<
321 			"F16(F32) = 0x" << std::setw(4) << nan_f16;
322 	}
323 }
324 
TEST(FP16_IEEE_FROM_FP32_VALUE,negative_nan)325 TEST(FP16_IEEE_FROM_FP32_VALUE, negative_nan) {
326 	for (uint32_t nan_f32 = UINT32_C(0xFFFFFFFF); nan_f32 > UINT32_C(0xFF800000); nan_f32--) {
327 		float nan_value;
328 		memcpy(&nan_value, &nan_f32, sizeof(nan_value));
329 		const uint16_t nan_f16 = fp16_ieee_from_fp32_value(nan_value);
330 
331 		/* Check sign */
332 		ASSERT_EQ(nan_f16 & UINT16_C(0x8000), UINT16_C(0x8000)) <<
333 			std::hex << std::uppercase << std::setfill('0') <<
334 			"F32 = 0x" << std::setw(8) << nan_f32 << ", " <<
335 			"F16(F32) = 0x" << std::setw(4) << nan_f16;
336 
337 		/* Check exponent */
338 		ASSERT_EQ(nan_f16 & UINT16_C(0x7C00), UINT16_C(0x7C00)) <<
339 			std::hex << std::uppercase << std::setfill('0') <<
340 			"F32 = 0x" << std::setw(8) << nan_f32 << ", " <<
341 			"F16(F32) = 0x" << std::setw(4) << nan_f16;
342 
343 		/* Check mantissa */
344 		ASSERT_NE(nan_f16 & UINT16_C(0x03FF), 0) <<
345 			std::hex << std::uppercase << std::setfill('0') <<
346 			"F32 = 0x" << std::setw(8) << nan_f32 << ", " <<
347 			"F16(F32) = 0x" << std::setw(4) << nan_f16;
348 	}
349 }
350 
TEST(FP16_IEEE_FROM_FP32_VALUE,revertible)351 TEST(FP16_IEEE_FROM_FP32_VALUE, revertible) {
352 	/* Positive values */
353 	for (uint16_t f16 = UINT16_C(0x0000); f16 < UINT16_C(0x7C00); f16++) {
354 		const float value_f32 = fp16_ieee_to_fp32_value(f16);
355 		uint32_t bits_f32;
356 		memcpy(&bits_f32, &value_f32, sizeof(bits_f32));
357 
358 		ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value_f32)) <<
359 			std::hex << std::uppercase << std::setfill('0') <<
360 			"F16 = 0x" << std::setw(4) << f16 << ", " <<
361 			"F32(F16) = 0x" << std::setw(8) << bits_f32 << ", " <<
362 			"F16(F32(F16)) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value_f32);
363 	}
364 
365 	/* Negative values */
366 	for (uint16_t f16 = UINT16_C(0x8000); f16 < UINT16_C(0xFC00); f16++) {
367 		const float value_f32 = fp16_ieee_to_fp32_value(f16);
368 		uint32_t bits_f32;
369 		memcpy(&bits_f32, &value_f32, sizeof(bits_f32));
370 
371 		ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value_f32)) <<
372 			std::hex << std::uppercase << std::setfill('0') <<
373 			"F16 = 0x" << std::setw(4) << f16 << ", " <<
374 			"F32(F16) = 0x" << std::setw(8) << bits_f32 << ", " <<
375 			"F16(F32(F16)) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value_f32);
376 	}
377 }
378 
TEST(FP16_IEEE_FROM_FP32_VALUE,underflow)379 TEST(FP16_IEEE_FROM_FP32_VALUE, underflow) {
380 	const uint32_t min_nonzero_f32 = UINT32_C(0x33000001);
381 	const uint16_t zero_f16 = UINT16_C(0x0000);
382 	const uint16_t min_f16 = UINT16_C(0x0001);
383 	for (uint32_t bits = UINT32_C(0x00000001); bits < min_nonzero_f32; bits++) {
384 		float value;
385 		memcpy(&value, &bits, sizeof(value));
386 		ASSERT_EQ(zero_f16, fp16_ieee_from_fp32_value(value)) <<
387 			std::hex << std::uppercase << std::setfill('0') <<
388 			"F32 = 0x" << std::setw(8) << bits << ", " <<
389 			"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " <<
390 			"F16 = 0x" << std::setw(4) << zero_f16;
391 	}
392 	float min_nonzero_value;
393 	memcpy(&min_nonzero_value, &min_nonzero_f32, sizeof(min_nonzero_value));
394 	ASSERT_EQ(min_f16, fp16_ieee_from_fp32_value(min_nonzero_value)) <<
395 		std::hex << std::uppercase << std::setfill('0') <<
396 		"F32 = 0x" << std::setw(8) << min_nonzero_f32 << ", " <<
397 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(min_nonzero_value) << ", " <<
398 		"F16 = 0x" << std::setw(4) << min_f16;
399 }
400 
TEST(FP16_IEEE_FROM_FP32_VALUE,overflow)401 TEST(FP16_IEEE_FROM_FP32_VALUE, overflow) {
402 	const uint32_t max_finite_f32 = UINT32_C(0x477FEFFF);
403 	const uint16_t max_finite_f16 = UINT16_C(0x7BFF);
404 	const uint32_t positive_infinity_f32 = UINT32_C(0x7F800000);
405 	const uint16_t positive_infinity_f16 = UINT16_C(0x7C00);
406 	for (uint32_t bits = positive_infinity_f32; bits > max_finite_f32; bits--) {
407 		float value;
408 		memcpy(&value, &bits, sizeof(value));
409 		ASSERT_EQ(positive_infinity_f16, fp16_ieee_from_fp32_value(value)) <<
410 			std::hex << std::uppercase << std::setfill('0') <<
411 			"F32 = 0x" << std::setw(8) << bits << ", " <<
412 			"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " <<
413 			"F16 = 0x" << std::setw(4) << positive_infinity_f16;
414 	}
415 	float max_finite_value;
416 	memcpy(&max_finite_value, &max_finite_f32, sizeof(max_finite_value));
417 	ASSERT_EQ(max_finite_f16, fp16_ieee_from_fp32_value(max_finite_value)) <<
418 		std::hex << std::uppercase << std::setfill('0') <<
419 		"F32 = 0x" << std::setw(8) << max_finite_f32 << ", " <<
420 		"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(max_finite_value) << ", " <<
421 		"F16 = 0x" << std::setw(4) << max_finite_f16;
422 }
423 
TEST(FP16_IEEE_FROM_FP32_VALUE,positive_denormalized_values)424 TEST(FP16_IEEE_FROM_FP32_VALUE, positive_denormalized_values) {
425 	const uint32_t min_nonzero_f32 = UINT32_C(0x33000001);
426 
427 	uint32_t f32_begin = min_nonzero_f32;
428 	for (uint16_t f16 = 0; f16 < UINT16_C(0x0400); f16++) {
429 		const uint32_t f32_end = fp16::denormalizedRanges[f16];
430 		for (uint32_t f32 = f32_begin; f32 < f32_end; f32++) {
431 			float value;
432 			memcpy(&value, &f32, sizeof(value));
433 			ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value)) <<
434 				std::hex << std::uppercase << std::setfill('0') <<
435 				"F32 = 0x" << std::setw(8) << f32 << ", " <<
436 				"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " <<
437 				"F16 = 0x" << std::setw(4) << f16;
438 		}
439 		f32_begin = f32_end;
440 	}
441 }
442 
TEST(FP16_IEEE_FROM_FP32_VALUE,negative_denormalized_values)443 TEST(FP16_IEEE_FROM_FP32_VALUE, negative_denormalized_values) {
444 	const uint32_t min_nonzero_f32 = UINT32_C(0x33000001);
445 
446 	uint32_t f32_begin = min_nonzero_f32 | UINT32_C(0x80000000);
447 	for (uint16_t f16 = UINT16_C(0x8000); f16 < UINT16_C(0x8400); f16++) {
448 		const uint32_t f32_end = fp16::denormalizedRanges[f16 & UINT16_C(0x7FFF)] | UINT32_C(0x80000000);
449 		for (uint32_t f32 = f32_begin; f32 < f32_end; f32++) {
450 			float value;
451 			memcpy(&value, &f32, sizeof(value));
452 			ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value)) <<
453 				std::hex << std::uppercase << std::setfill('0') <<
454 				"F32 = 0x" << std::setw(8) << f32 << ", " <<
455 				"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " <<
456 				"F16 = 0x" << std::setw(4) << f16;
457 		}
458 		f32_begin = f32_end;
459 	}
460 }
461 
TEST(FP16_IEEE_FROM_FP32_VALUE,positive_normalized_values)462 TEST(FP16_IEEE_FROM_FP32_VALUE, positive_normalized_values) {
463 	/* Minimum number that rounds to 1.0h when converted to half-precision */
464 	const uint32_t min_one_f32 = UINT32_C(0x3F7FF000);
465 	const uint32_t e_bias = 15;
466 
467 	for (int32_t e = -14; e <= 15; e++) {
468 		uint32_t f32_begin = min_one_f32 + (uint32_t(e) << 23);
469 		for (uint16_t f16 = uint16_t(e + e_bias) << 10; f16 < uint16_t(e + e_bias + 1) << 10; f16++) {
470 			const uint32_t f32_end = fp16::normalizedRanges[f16 & UINT16_C(0x3FF)] + (uint32_t(e) << 23);
471 			for (uint32_t f32 = f32_begin; f32 < f32_end; f32++) {
472 				float value;
473 				memcpy(&value, &f32, sizeof(value));
474 				ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value)) <<
475 					std::hex << std::uppercase << std::setfill('0') <<
476 					"F32 = 0x" << std::setw(8) << f32 << ", " <<
477 					"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " <<
478 					"F16 = 0x" << std::setw(4) << f16;
479 			}
480 			f32_begin = f32_end;
481 		}
482 	}
483 }
484 
TEST(FP16_IEEE_FROM_FP32_VALUE,negative_normalized_values)485 TEST(FP16_IEEE_FROM_FP32_VALUE, negative_normalized_values) {
486 	/* Minimum number that rounds to 1.0h when converted to half-precision */
487 	const uint32_t min_one_f32 = UINT32_C(0x3F7FF000);
488 	const uint32_t e_bias = 15;
489 
490 	for (int32_t e = -14; e <= 15; e++) {
491 		uint32_t f32_begin = (min_one_f32 | UINT32_C(0x80000000)) + (uint32_t(e) << 23);
492 		for (uint16_t f16 = (UINT16_C(0x8000) | (uint16_t(e + e_bias) << 10)); f16 < (UINT16_C(0x8000) | (uint16_t(e + e_bias + 1) << 10)); f16++) {
493 			const uint32_t f32_end = (fp16::normalizedRanges[f16 & UINT16_C(0x3FF)] | UINT32_C(0x80000000)) + (uint32_t(e) << 23);
494 			for (uint32_t f32 = f32_begin; f32 < f32_end; f32++) {
495 				float value;
496 				memcpy(&value, &f32, sizeof(value));
497 				ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value)) <<
498 					std::hex << std::uppercase << std::setfill('0') <<
499 					"F32 = 0x" << std::setw(8) << f32 << ", " <<
500 					"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " <<
501 					"F16 = 0x" << std::setw(4) << f16;
502 			}
503 			f32_begin = f32_end;
504 		}
505 	}
506 }
507 
508 #if (defined(__i386__) || defined(__x86_64__)) && defined(__F16C__)
TEST(FP16_IEEE_FROM_FP32_VALUE,match_hardware)509 	TEST(FP16_IEEE_FROM_FP32_VALUE, match_hardware) {
510 		const uint32_t min_nonzero = UINT32_C(0x00000001);
511 		const uint32_t max_finite = UINT32_C(0x7F800000);
512 		for (uint32_t bits = min_nonzero; bits < max_finite; bits++) {
513 			float value;
514 			memcpy(&value, &bits, sizeof(value));
515 			const uint16_t reference = uint16_t(_mm_cvtsi128_si32(_mm_cvtps_ph(_mm_set_ss(value), _MM_FROUND_CUR_DIRECTION)));
516 			ASSERT_EQ(reference, fp16_ieee_from_fp32_value(value)) <<
517 				std::hex << std::uppercase << std::setfill('0') <<
518 				"F32 = 0x" << std::setw(8) << bits << ", " <<
519 				"F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " <<
520 				"F16 = 0x" << std::setw(4) << reference;
521 		}
522 	}
523 #endif
524