| #include <gtest/gtest.h> |
| |
| #include <cstdint> |
| |
| #include <fp16.h> |
| #include "tables.h" |
| |
| #if (defined(__i386__) || defined(__x86_64__)) && defined(__F16C__) |
| #include <x86intrin.h> |
| #endif |
| |
| |
| TEST(FP16_IEEE_FROM_FP32_VALUE, normalized_powers_of_2) { |
| const uint16_t min_po2_f16 = UINT16_C(0x0400); |
| const uint16_t eighths_f16 = UINT16_C(0x3000); |
| const uint16_t quarter_f16 = UINT16_C(0x3400); |
| const uint16_t half_f16 = UINT16_C(0x3800); |
| const uint16_t one_f16 = UINT16_C(0x3C00); |
| const uint16_t two_f16 = UINT16_C(0x4000); |
| const uint16_t four_f16 = UINT16_C(0x4400); |
| const uint16_t eight_f16 = UINT16_C(0x4800); |
| const uint16_t sixteen_f16 = UINT16_C(0x4C00); |
| const uint16_t thirtytwo_f16 = UINT16_C(0x5000); |
| const uint16_t sixtyfour_f16 = UINT16_C(0x5400); |
| const uint16_t max_po2_f16 = UINT16_C(0x7800); |
| |
| const uint32_t min_po2_f32 = UINT32_C(0x38800000); |
| const uint32_t eighths_f32 = UINT32_C(0x3E000000); |
| const uint32_t quarter_f32 = UINT32_C(0x3E800000); |
| const uint32_t half_f32 = UINT32_C(0x3F000000); |
| const uint32_t one_f32 = UINT32_C(0x3F800000); |
| const uint32_t two_f32 = UINT32_C(0x40000000); |
| const uint32_t four_f32 = UINT32_C(0x40800000); |
| const uint32_t eight_f32 = UINT32_C(0x41000000); |
| const uint32_t sixteen_f32 = UINT32_C(0x41800000); |
| const uint32_t thirtytwo_f32 = UINT32_C(0x42000000); |
| const uint32_t sixtyfour_f32 = UINT32_C(0x42800000); |
| const uint32_t max_po2_f32 = UINT32_C(0x47000000); |
| |
| float min_po2_value; |
| memcpy(&min_po2_value, &min_po2_f32, sizeof(min_po2_value)); |
| EXPECT_EQ(min_po2_f16, fp16_ieee_from_fp32_value(min_po2_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << min_po2_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(min_po2_value) << ", " << |
| "F16 = 0x" << std::setw(4) << min_po2_f16; |
| |
| float eighths_value; |
| memcpy(&eighths_value, &eighths_f32, sizeof(eighths_value)); |
| EXPECT_EQ(eighths_f16, fp16_ieee_from_fp32_value(eighths_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << eighths_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(eighths_value) << ", " << |
| "F16 = 0x" << std::setw(4) << eighths_f16; |
| |
| float quarter_value; |
| memcpy(&quarter_value, &quarter_f32, sizeof(quarter_value)); |
| EXPECT_EQ(quarter_f16, fp16_ieee_from_fp32_value(quarter_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << quarter_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(quarter_value) << ", " << |
| "F16 = 0x" << std::setw(4) << quarter_f16; |
| |
| float half_value; |
| memcpy(&half_value, &half_f32, sizeof(half_value)); |
| EXPECT_EQ(half_f16, fp16_ieee_from_fp32_value(half_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << half_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(half_value) << ", " << |
| "F16 = 0x" << std::setw(4) << half_f16; |
| |
| float one_value; |
| memcpy(&one_value, &one_f32, sizeof(one_value)); |
| EXPECT_EQ(one_f16, fp16_ieee_from_fp32_value(one_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << one_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(one_value) << ", " << |
| "F16 = 0x" << std::setw(4) << one_f16; |
| |
| float two_value; |
| memcpy(&two_value, &two_f32, sizeof(two_value)); |
| EXPECT_EQ(two_f16, fp16_ieee_from_fp32_value(two_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << two_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(two_value) << ", " << |
| "F16 = 0x" << std::setw(4) << two_f16; |
| |
| float four_value; |
| memcpy(&four_value, &four_f32, sizeof(four_value)); |
| EXPECT_EQ(four_f16, fp16_ieee_from_fp32_value(four_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << four_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(four_value) << ", " << |
| "F16 = 0x" << std::setw(4) << four_f16; |
| |
| float eight_value; |
| memcpy(&eight_value, &eight_f32, sizeof(eight_value)); |
| EXPECT_EQ(eight_f16, fp16_ieee_from_fp32_value(eight_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << eight_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(eight_value) << ", " << |
| "F16 = 0x" << std::setw(4) << eight_f16; |
| |
| float sixteen_value; |
| memcpy(&sixteen_value, &sixteen_f32, sizeof(sixteen_value)); |
| EXPECT_EQ(sixteen_f16, fp16_ieee_from_fp32_value(sixteen_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << sixteen_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(sixteen_value) << ", " << |
| "F16 = 0x" << std::setw(4) << sixteen_f16; |
| |
| float thirtytwo_value; |
| memcpy(&thirtytwo_value, &thirtytwo_f32, sizeof(thirtytwo_value)); |
| EXPECT_EQ(thirtytwo_f16, fp16_ieee_from_fp32_value(thirtytwo_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << thirtytwo_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(thirtytwo_value) << ", " << |
| "F16 = 0x" << std::setw(4) << thirtytwo_f16; |
| |
| float sixtyfour_value; |
| memcpy(&sixtyfour_value, &sixtyfour_f32, sizeof(sixtyfour_value)); |
| EXPECT_EQ(sixtyfour_f16, fp16_ieee_from_fp32_value(sixtyfour_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << sixtyfour_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(sixtyfour_value) << ", " << |
| "F16 = 0x" << std::setw(4) << sixtyfour_f16; |
| |
| float max_po2_value; |
| memcpy(&max_po2_value, &max_po2_f32, sizeof(max_po2_value)); |
| EXPECT_EQ(max_po2_f16, fp16_ieee_from_fp32_value(max_po2_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << max_po2_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(max_po2_value) << ", " << |
| "F16 = 0x" << std::setw(4) << max_po2_f16; |
| } |
| |
| TEST(FP16_IEEE_FROM_FP32_VALUE, denormalized_powers_of_2) { |
| const uint16_t exp2_minus_15_f16 = UINT16_C(0x0200); |
| const uint16_t exp2_minus_16_f16 = UINT16_C(0x0100); |
| const uint16_t exp2_minus_17_f16 = UINT16_C(0x0080); |
| const uint16_t exp2_minus_18_f16 = UINT16_C(0x0040); |
| const uint16_t exp2_minus_19_f16 = UINT16_C(0x0020); |
| const uint16_t exp2_minus_20_f16 = UINT16_C(0x0010); |
| const uint16_t exp2_minus_21_f16 = UINT16_C(0x0008); |
| const uint16_t exp2_minus_22_f16 = UINT16_C(0x0004); |
| const uint16_t exp2_minus_23_f16 = UINT16_C(0x0002); |
| const uint16_t exp2_minus_24_f16 = UINT16_C(0x0001); |
| const uint16_t exp2_minus_25_f16 = UINT16_C(0x0000); |
| |
| const uint32_t exp2_minus_15_f32 = UINT32_C(0x38000000); |
| const uint32_t exp2_minus_16_f32 = UINT32_C(0x37800000); |
| const uint32_t exp2_minus_17_f32 = UINT32_C(0x37000000); |
| const uint32_t exp2_minus_18_f32 = UINT32_C(0x36800000); |
| const uint32_t exp2_minus_19_f32 = UINT32_C(0x36000000); |
| const uint32_t exp2_minus_20_f32 = UINT32_C(0x35800000); |
| const uint32_t exp2_minus_21_f32 = UINT32_C(0x35000000); |
| const uint32_t exp2_minus_22_f32 = UINT32_C(0x34800000); |
| const uint32_t exp2_minus_23_f32 = UINT32_C(0x34000000); |
| const uint32_t exp2_minus_24_f32 = UINT32_C(0x33800000); |
| const uint32_t exp2_minus_25_f32 = UINT32_C(0x33000000); |
| |
| float exp2_minus_15_value; |
| memcpy(&exp2_minus_15_value, &exp2_minus_15_f32, sizeof(exp2_minus_15_value)); |
| EXPECT_EQ(exp2_minus_15_f16, fp16_ieee_from_fp32_value(exp2_minus_15_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << exp2_minus_15_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_15_value) << ", " << |
| "F16 = 0x" << std::setw(4) << exp2_minus_15_f16; |
| |
| float exp2_minus_16_value; |
| memcpy(&exp2_minus_16_value, &exp2_minus_16_f32, sizeof(exp2_minus_16_value)); |
| EXPECT_EQ(exp2_minus_16_f16, fp16_ieee_from_fp32_value(exp2_minus_16_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << exp2_minus_16_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_16_value) << ", " << |
| "F16 = 0x" << std::setw(4) << exp2_minus_16_f16; |
| |
| float exp2_minus_17_value; |
| memcpy(&exp2_minus_17_value, &exp2_minus_17_f32, sizeof(exp2_minus_17_value)); |
| EXPECT_EQ(exp2_minus_17_f16, fp16_ieee_from_fp32_value(exp2_minus_17_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << exp2_minus_17_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_17_value) << ", " << |
| "F16 = 0x" << std::setw(4) << exp2_minus_17_f16; |
| |
| float exp2_minus_18_value; |
| memcpy(&exp2_minus_18_value, &exp2_minus_18_f32, sizeof(exp2_minus_18_value)); |
| EXPECT_EQ(exp2_minus_18_f16, fp16_ieee_from_fp32_value(exp2_minus_18_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << exp2_minus_18_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_18_value) << ", " << |
| "F16 = 0x" << std::setw(4) << exp2_minus_18_f16; |
| |
| float exp2_minus_19_value; |
| memcpy(&exp2_minus_19_value, &exp2_minus_19_f32, sizeof(exp2_minus_19_value)); |
| EXPECT_EQ(exp2_minus_19_f16, fp16_ieee_from_fp32_value(exp2_minus_19_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << exp2_minus_19_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_19_value) << ", " << |
| "F16 = 0x" << std::setw(4) << exp2_minus_19_f16; |
| |
| float exp2_minus_20_value; |
| memcpy(&exp2_minus_20_value, &exp2_minus_20_f32, sizeof(exp2_minus_20_value)); |
| EXPECT_EQ(exp2_minus_20_f16, fp16_ieee_from_fp32_value(exp2_minus_20_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << exp2_minus_20_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_20_value) << ", " << |
| "F16 = 0x" << std::setw(4) << exp2_minus_20_f16; |
| |
| float exp2_minus_21_value; |
| memcpy(&exp2_minus_21_value, &exp2_minus_21_f32, sizeof(exp2_minus_21_value)); |
| EXPECT_EQ(exp2_minus_21_f16, fp16_ieee_from_fp32_value(exp2_minus_21_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << exp2_minus_21_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_21_value) << ", " << |
| "F16 = 0x" << std::setw(4) << exp2_minus_21_f16; |
| |
| float exp2_minus_22_value; |
| memcpy(&exp2_minus_22_value, &exp2_minus_22_f32, sizeof(exp2_minus_22_value)); |
| EXPECT_EQ(exp2_minus_22_f16, fp16_ieee_from_fp32_value(exp2_minus_22_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << exp2_minus_22_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_22_value) << ", " << |
| "F16 = 0x" << std::setw(4) << exp2_minus_22_f16; |
| |
| float exp2_minus_23_value; |
| memcpy(&exp2_minus_23_value, &exp2_minus_23_f32, sizeof(exp2_minus_23_value)); |
| EXPECT_EQ(exp2_minus_23_f16, fp16_ieee_from_fp32_value(exp2_minus_23_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << exp2_minus_23_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_23_value) << ", " << |
| "F16 = 0x" << std::setw(4) << exp2_minus_23_f16; |
| |
| float exp2_minus_24_value; |
| memcpy(&exp2_minus_24_value, &exp2_minus_24_f32, sizeof(exp2_minus_24_value)); |
| EXPECT_EQ(exp2_minus_24_f16, fp16_ieee_from_fp32_value(exp2_minus_24_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << exp2_minus_24_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_24_value) << ", " << |
| "F16 = 0x" << std::setw(4) << exp2_minus_24_f16; |
| |
| float exp2_minus_25_value; |
| memcpy(&exp2_minus_25_value, &exp2_minus_25_f32, sizeof(exp2_minus_25_value)); |
| EXPECT_EQ(exp2_minus_25_f16, fp16_ieee_from_fp32_value(exp2_minus_25_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << exp2_minus_25_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(exp2_minus_25_value) << ", " << |
| "F16 = 0x" << std::setw(4) << exp2_minus_25_f16; |
| } |
| |
| TEST(FP16_IEEE_FROM_FP32_VALUE, zero) { |
| const uint16_t positive_zero_f16 = UINT16_C(0x0000); |
| const uint16_t negative_zero_f16 = UINT16_C(0x8000); |
| |
| const uint32_t positive_zero_f32 = UINT32_C(0x00000000); |
| const uint32_t negative_zero_f32 = UINT32_C(0x80000000); |
| |
| float positive_zero_value; |
| memcpy(&positive_zero_value, &positive_zero_f32, sizeof(positive_zero_value)); |
| EXPECT_EQ(positive_zero_f16, fp16_ieee_from_fp32_value(positive_zero_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << positive_zero_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(positive_zero_value) << ", " << |
| "F16 = 0x" << std::setw(4) << positive_zero_f16; |
| |
| float negative_zero_value; |
| memcpy(&negative_zero_value, &negative_zero_f32, sizeof(negative_zero_value)); |
| EXPECT_EQ(negative_zero_f16, fp16_ieee_from_fp32_value(negative_zero_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << negative_zero_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(negative_zero_value) << ", " << |
| "F16 = 0x" << std::setw(4) << negative_zero_f16; |
| } |
| |
| TEST(FP16_IEEE_FROM_FP32_VALUE, infinity) { |
| const uint16_t positive_infinity_f16 = UINT16_C(0x7C00); |
| const uint16_t negative_infinity_f16 = UINT16_C(0xFC00); |
| |
| const uint32_t positive_infinity_f32 = UINT32_C(0x7F800000); |
| const uint32_t negative_infinity_f32 = UINT32_C(0xFF800000); |
| |
| float positive_infinity_value; |
| memcpy(&positive_infinity_value, &positive_infinity_f32, sizeof(positive_infinity_value)); |
| EXPECT_EQ(positive_infinity_f16, fp16_ieee_from_fp32_value(positive_infinity_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << positive_infinity_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(positive_infinity_value) << ", " << |
| "F16 = 0x" << std::setw(4) << positive_infinity_f16; |
| |
| float negative_infinity_value; |
| memcpy(&negative_infinity_value, &negative_infinity_f32, sizeof(negative_infinity_value)); |
| EXPECT_EQ(negative_infinity_f16, fp16_ieee_from_fp32_value(negative_infinity_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << negative_infinity_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(negative_infinity_value) << ", " << |
| "F16 = 0x" << std::setw(4) << negative_infinity_f16; |
| } |
| |
| TEST(FP16_IEEE_FROM_FP32_VALUE, positive_nan) { |
| for (uint32_t nan_f32 = UINT32_C(0x7FFFFFFF); nan_f32 > UINT32_C(0x7F800000); nan_f32--) { |
| float nan_value; |
| memcpy(&nan_value, &nan_f32, sizeof(nan_value)); |
| const uint16_t nan_f16 = fp16_ieee_from_fp32_value(nan_value); |
| |
| /* Check sign */ |
| ASSERT_EQ(nan_f16 & UINT16_C(0x8000), 0) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << nan_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << nan_f16; |
| |
| /* Check exponent */ |
| ASSERT_EQ(nan_f16 & UINT16_C(0x7C00), UINT16_C(0x7C00)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << nan_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << nan_f16; |
| |
| /* Check mantissa */ |
| ASSERT_NE(nan_f16 & UINT16_C(0x03FF), 0) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << nan_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << nan_f16; |
| } |
| } |
| |
| TEST(FP16_IEEE_FROM_FP32_VALUE, negative_nan) { |
| for (uint32_t nan_f32 = UINT32_C(0xFFFFFFFF); nan_f32 > UINT32_C(0xFF800000); nan_f32--) { |
| float nan_value; |
| memcpy(&nan_value, &nan_f32, sizeof(nan_value)); |
| const uint16_t nan_f16 = fp16_ieee_from_fp32_value(nan_value); |
| |
| /* Check sign */ |
| ASSERT_EQ(nan_f16 & UINT16_C(0x8000), UINT16_C(0x8000)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << nan_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << nan_f16; |
| |
| /* Check exponent */ |
| ASSERT_EQ(nan_f16 & UINT16_C(0x7C00), UINT16_C(0x7C00)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << nan_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << nan_f16; |
| |
| /* Check mantissa */ |
| ASSERT_NE(nan_f16 & UINT16_C(0x03FF), 0) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << nan_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << nan_f16; |
| } |
| } |
| |
| TEST(FP16_IEEE_FROM_FP32_VALUE, revertible) { |
| /* Positive values */ |
| for (uint16_t f16 = UINT16_C(0x0000); f16 < UINT16_C(0x7C00); f16++) { |
| const float value_f32 = fp16_ieee_to_fp32_value(f16); |
| uint32_t bits_f32; |
| memcpy(&bits_f32, &value_f32, sizeof(bits_f32)); |
| |
| ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value_f32)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F16 = 0x" << std::setw(4) << f16 << ", " << |
| "F32(F16) = 0x" << std::setw(8) << bits_f32 << ", " << |
| "F16(F32(F16)) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value_f32); |
| } |
| |
| /* Negative values */ |
| for (uint16_t f16 = UINT16_C(0x8000); f16 < UINT16_C(0xFC00); f16++) { |
| const float value_f32 = fp16_ieee_to_fp32_value(f16); |
| uint32_t bits_f32; |
| memcpy(&bits_f32, &value_f32, sizeof(bits_f32)); |
| |
| ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value_f32)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F16 = 0x" << std::setw(4) << f16 << ", " << |
| "F32(F16) = 0x" << std::setw(8) << bits_f32 << ", " << |
| "F16(F32(F16)) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value_f32); |
| } |
| } |
| |
| TEST(FP16_IEEE_FROM_FP32_VALUE, underflow) { |
| const uint32_t min_nonzero_f32 = UINT32_C(0x33000001); |
| const uint16_t zero_f16 = UINT16_C(0x0000); |
| const uint16_t min_f16 = UINT16_C(0x0001); |
| for (uint32_t bits = UINT32_C(0x00000001); bits < min_nonzero_f32; bits++) { |
| float value; |
| memcpy(&value, &bits, sizeof(value)); |
| ASSERT_EQ(zero_f16, fp16_ieee_from_fp32_value(value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << bits << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " << |
| "F16 = 0x" << std::setw(4) << zero_f16; |
| } |
| float min_nonzero_value; |
| memcpy(&min_nonzero_value, &min_nonzero_f32, sizeof(min_nonzero_value)); |
| ASSERT_EQ(min_f16, fp16_ieee_from_fp32_value(min_nonzero_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << min_nonzero_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(min_nonzero_value) << ", " << |
| "F16 = 0x" << std::setw(4) << min_f16; |
| } |
| |
| TEST(FP16_IEEE_FROM_FP32_VALUE, overflow) { |
| const uint32_t max_finite_f32 = UINT32_C(0x477FEFFF); |
| const uint16_t max_finite_f16 = UINT16_C(0x7BFF); |
| const uint32_t positive_infinity_f32 = UINT32_C(0x7F800000); |
| const uint16_t positive_infinity_f16 = UINT16_C(0x7C00); |
| for (uint32_t bits = positive_infinity_f32; bits > max_finite_f32; bits--) { |
| float value; |
| memcpy(&value, &bits, sizeof(value)); |
| ASSERT_EQ(positive_infinity_f16, fp16_ieee_from_fp32_value(value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << bits << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " << |
| "F16 = 0x" << std::setw(4) << positive_infinity_f16; |
| } |
| float max_finite_value; |
| memcpy(&max_finite_value, &max_finite_f32, sizeof(max_finite_value)); |
| ASSERT_EQ(max_finite_f16, fp16_ieee_from_fp32_value(max_finite_value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << max_finite_f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(max_finite_value) << ", " << |
| "F16 = 0x" << std::setw(4) << max_finite_f16; |
| } |
| |
| TEST(FP16_IEEE_FROM_FP32_VALUE, positive_denormalized_values) { |
| const uint32_t min_nonzero_f32 = UINT32_C(0x33000001); |
| |
| uint32_t f32_begin = min_nonzero_f32; |
| for (uint16_t f16 = 0; f16 < UINT16_C(0x0400); f16++) { |
| const uint32_t f32_end = fp16::denormalizedRanges[f16]; |
| for (uint32_t f32 = f32_begin; f32 < f32_end; f32++) { |
| float value; |
| memcpy(&value, &f32, sizeof(value)); |
| ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " << |
| "F16 = 0x" << std::setw(4) << f16; |
| } |
| f32_begin = f32_end; |
| } |
| } |
| |
| TEST(FP16_IEEE_FROM_FP32_VALUE, negative_denormalized_values) { |
| const uint32_t min_nonzero_f32 = UINT32_C(0x33000001); |
| |
| uint32_t f32_begin = min_nonzero_f32 | UINT32_C(0x80000000); |
| for (uint16_t f16 = UINT16_C(0x8000); f16 < UINT16_C(0x8400); f16++) { |
| const uint32_t f32_end = fp16::denormalizedRanges[f16 & UINT16_C(0x7FFF)] | UINT32_C(0x80000000); |
| for (uint32_t f32 = f32_begin; f32 < f32_end; f32++) { |
| float value; |
| memcpy(&value, &f32, sizeof(value)); |
| ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " << |
| "F16 = 0x" << std::setw(4) << f16; |
| } |
| f32_begin = f32_end; |
| } |
| } |
| |
| TEST(FP16_IEEE_FROM_FP32_VALUE, positive_normalized_values) { |
| /* Minimum number that rounds to 1.0h when converted to half-precision */ |
| const uint32_t min_one_f32 = UINT32_C(0x3F7FF000); |
| const uint32_t e_bias = 15; |
| |
| for (int32_t e = -14; e <= 15; e++) { |
| uint32_t f32_begin = min_one_f32 + (uint32_t(e) << 23); |
| for (uint16_t f16 = uint16_t(e + e_bias) << 10; f16 < uint16_t(e + e_bias + 1) << 10; f16++) { |
| const uint32_t f32_end = fp16::normalizedRanges[f16 & UINT16_C(0x3FF)] + (uint32_t(e) << 23); |
| for (uint32_t f32 = f32_begin; f32 < f32_end; f32++) { |
| float value; |
| memcpy(&value, &f32, sizeof(value)); |
| ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " << |
| "F16 = 0x" << std::setw(4) << f16; |
| } |
| f32_begin = f32_end; |
| } |
| } |
| } |
| |
| TEST(FP16_IEEE_FROM_FP32_VALUE, negative_normalized_values) { |
| /* Minimum number that rounds to 1.0h when converted to half-precision */ |
| const uint32_t min_one_f32 = UINT32_C(0x3F7FF000); |
| const uint32_t e_bias = 15; |
| |
| for (int32_t e = -14; e <= 15; e++) { |
| uint32_t f32_begin = (min_one_f32 | UINT32_C(0x80000000)) + (uint32_t(e) << 23); |
| 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++) { |
| const uint32_t f32_end = (fp16::normalizedRanges[f16 & UINT16_C(0x3FF)] | UINT32_C(0x80000000)) + (uint32_t(e) << 23); |
| for (uint32_t f32 = f32_begin; f32 < f32_end; f32++) { |
| float value; |
| memcpy(&value, &f32, sizeof(value)); |
| ASSERT_EQ(f16, fp16_ieee_from_fp32_value(value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << f32 << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " << |
| "F16 = 0x" << std::setw(4) << f16; |
| } |
| f32_begin = f32_end; |
| } |
| } |
| } |
| |
| #if (defined(__i386__) || defined(__x86_64__)) && defined(__F16C__) |
| TEST(FP16_IEEE_FROM_FP32_VALUE, match_hardware) { |
| const uint32_t min_nonzero = UINT32_C(0x00000001); |
| const uint32_t max_finite = UINT32_C(0x7F800000); |
| for (uint32_t bits = min_nonzero; bits < max_finite; bits++) { |
| float value; |
| memcpy(&value, &bits, sizeof(value)); |
| const uint16_t reference = uint16_t(_mm_cvtsi128_si32(_mm_cvtps_ph(_mm_set_ss(value), _MM_FROUND_CUR_DIRECTION))); |
| ASSERT_EQ(reference, fp16_ieee_from_fp32_value(value)) << |
| std::hex << std::uppercase << std::setfill('0') << |
| "F32 = 0x" << std::setw(8) << bits << ", " << |
| "F16(F32) = 0x" << std::setw(4) << fp16_ieee_from_fp32_value(value) << ", " << |
| "F16 = 0x" << std::setw(4) << reference; |
| } |
| } |
| #endif |