1 | //===-- Utility class to test different flavors of fma --------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef LLVM_LIBC_TEST_SRC_MATH_FMATEST_H |
10 | #define LLVM_LIBC_TEST_SRC_MATH_FMATEST_H |
11 | |
12 | #include "src/__support/FPUtil/cast.h" |
13 | #include "test/UnitTest/FEnvSafeTest.h" |
14 | #include "test/UnitTest/FPMatcher.h" |
15 | #include "test/UnitTest/Test.h" |
16 | |
17 | template <typename OutType, typename InType = OutType> |
18 | class FmaTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest { |
19 | |
20 | struct OutConstants { |
21 | DECLARE_SPECIAL_CONSTANTS(OutType) |
22 | }; |
23 | |
24 | struct InConstants { |
25 | DECLARE_SPECIAL_CONSTANTS(InType) |
26 | }; |
27 | |
28 | using OutFPBits = typename OutConstants::FPBits; |
29 | using OutStorageType = typename OutConstants::StorageType; |
30 | using InFPBits = typename InConstants::FPBits; |
31 | using InStorageType = typename InConstants::StorageType; |
32 | |
33 | static constexpr OutStorageType OUT_MIN_NORMAL_U = |
34 | OutFPBits::min_normal().uintval(); |
35 | static constexpr InStorageType IN_MIN_NORMAL_U = |
36 | InFPBits::min_normal().uintval(); |
37 | |
38 | OutConstants out; |
39 | InConstants in; |
40 | |
41 | const InType in_out_min_normal = |
42 | LIBC_NAMESPACE::fputil::cast<InType>(out.min_normal); |
43 | const InType in_out_min_denormal = |
44 | LIBC_NAMESPACE::fputil::cast<InType>(out.min_denormal); |
45 | |
46 | public: |
47 | using FmaFunc = OutType (*)(InType, InType, InType); |
48 | |
49 | void test_special_numbers(FmaFunc func) { |
50 | EXPECT_FP_EQ(out.zero, func(in.zero, in.zero, in.zero)); |
51 | EXPECT_FP_EQ(out.neg_zero, func(in.zero, in.neg_zero, in.neg_zero)); |
52 | EXPECT_FP_EQ(out.inf, func(in.inf, in.inf, in.zero)); |
53 | EXPECT_FP_EQ(out.neg_inf, func(in.neg_inf, in.inf, in.neg_inf)); |
54 | EXPECT_FP_EQ(out.aNaN, func(in.inf, in.zero, in.zero)); |
55 | EXPECT_FP_EQ(out.aNaN, func(in.inf, in.neg_inf, in.inf)); |
56 | EXPECT_FP_EQ(out.aNaN, func(in.aNaN, in.zero, in.inf)); |
57 | EXPECT_FP_EQ(out.aNaN, func(in.inf, in.neg_inf, in.aNaN)); |
58 | |
59 | // Test underflow rounding up. |
60 | EXPECT_FP_EQ(OutFPBits(OutStorageType(2)).get_val(), |
61 | func(InType(0.5), in_out_min_denormal, in_out_min_denormal)); |
62 | |
63 | if constexpr (sizeof(OutType) < sizeof(InType)) { |
64 | EXPECT_FP_EQ(out.zero, |
65 | func(InType(0.5), in.min_denormal, in.min_denormal)); |
66 | } |
67 | |
68 | // Test underflow rounding down. |
69 | OutType v = OutFPBits(static_cast<OutStorageType>(OUT_MIN_NORMAL_U + |
70 | OutStorageType(1))) |
71 | .get_val(); |
72 | EXPECT_FP_EQ(v, func(InType(1) / InType(OUT_MIN_NORMAL_U << 1), |
73 | LIBC_NAMESPACE::fputil::cast<InType>(v), |
74 | in_out_min_normal)); |
75 | |
76 | if constexpr (sizeof(OutType) < sizeof(InType)) { |
77 | InFPBits tmp = InFPBits::one(); |
78 | tmp.set_biased_exponent(InFPBits::EXP_BIAS - InFPBits::FRACTION_LEN - 1); |
79 | InType reciprocal_value = tmp.get_val(); |
80 | |
81 | InType v = InFPBits(static_cast<InStorageType>(IN_MIN_NORMAL_U + |
82 | InStorageType(1))) |
83 | .get_val(); |
84 | EXPECT_FP_EQ(out.min_normal, |
85 | func(reciprocal_value, v, in_out_min_normal)); |
86 | } |
87 | |
88 | // Test overflow. |
89 | OutType z = out.max_normal; |
90 | InType in_z = LIBC_NAMESPACE::fputil::cast<InType>(out.max_normal); |
91 | EXPECT_FP_EQ_ALL_ROUNDING(OutType(0.75) * z, |
92 | func(InType(1.75), in_z, -in_z)); |
93 | |
94 | // Exact cancellation. |
95 | EXPECT_FP_EQ_ROUNDING_NEAREST( |
96 | out.zero, func(InType(3.0), InType(5.0), InType(-15.0))); |
97 | EXPECT_FP_EQ_ROUNDING_UPWARD(out.zero, |
98 | func(InType(3.0), InType(5.0), InType(-15.0))); |
99 | EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO( |
100 | out.zero, func(InType(3.0), InType(5.0), InType(-15.0))); |
101 | EXPECT_FP_EQ_ROUNDING_DOWNWARD( |
102 | out.neg_zero, func(InType(3.0), InType(5.0), InType(-15.0))); |
103 | |
104 | EXPECT_FP_EQ_ROUNDING_NEAREST( |
105 | out.zero, func(InType(-3.0), InType(5.0), InType(15.0))); |
106 | EXPECT_FP_EQ_ROUNDING_UPWARD(out.zero, |
107 | func(InType(-3.0), InType(5.0), InType(15.0))); |
108 | EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO( |
109 | out.zero, func(InType(-3.0), InType(5.0), InType(15.0))); |
110 | EXPECT_FP_EQ_ROUNDING_DOWNWARD( |
111 | out.neg_zero, func(InType(-3.0), InType(5.0), InType(15.0))); |
112 | } |
113 | }; |
114 | |
115 | #define LIST_FMA_TESTS(T, func) \ |
116 | using LlvmLibcFmaTest = FmaTestTemplate<T>; \ |
117 | TEST_F(LlvmLibcFmaTest, SpecialNumbers) { test_special_numbers(&func); } |
118 | |
119 | #define LIST_NARROWING_FMA_TESTS(OutType, InType, func) \ |
120 | using LlvmLibcFmaTest = FmaTestTemplate<OutType, InType>; \ |
121 | TEST_F(LlvmLibcFmaTest, SpecialNumbers) { test_special_numbers(&func); } |
122 | |
123 | #endif // LLVM_LIBC_TEST_SRC_MATH_FMATEST_H |
124 | |