1 | //===-- FPMatchers.h --------------------------------------------*- C++ -*-===// |
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_UNITTEST_FPMATCHER_H |
10 | #define LLVM_LIBC_TEST_UNITTEST_FPMATCHER_H |
11 | |
12 | #include "src/__support/CPP/array.h" |
13 | #include "src/__support/CPP/type_traits.h" |
14 | #include "src/__support/FPUtil/FEnvImpl.h" |
15 | #include "src/__support/FPUtil/FPBits.h" |
16 | #include "src/__support/FPUtil/fpbits_str.h" |
17 | #include "test/UnitTest/RoundingModeUtils.h" |
18 | #include "test/UnitTest/StringUtils.h" |
19 | #include "test/UnitTest/Test.h" |
20 | |
21 | #include "hdr/math_macros.h" |
22 | |
23 | namespace LIBC_NAMESPACE { |
24 | namespace testing { |
25 | |
26 | template <typename T, TestCond Condition> class FPMatcher : public Matcher<T> { |
27 | static_assert(cpp::is_floating_point_v<T>, |
28 | "FPMatcher can only be used with floating point values." ); |
29 | static_assert(Condition == TestCond::EQ || Condition == TestCond::NE, |
30 | "Unsupported FPMatcher test condition." ); |
31 | |
32 | T expected; |
33 | T actual; |
34 | |
35 | public: |
36 | FPMatcher(T expectedValue) : expected(expectedValue) {} |
37 | |
38 | bool match(T actualValue) { |
39 | actual = actualValue; |
40 | fputil::FPBits<T> actualBits(actual), expectedBits(expected); |
41 | if (Condition == TestCond::EQ) |
42 | return (actualBits.is_nan() && expectedBits.is_nan()) || |
43 | (actualBits.uintval() == expectedBits.uintval()); |
44 | |
45 | // If condition == TestCond::NE. |
46 | if (actualBits.is_nan()) |
47 | return !expectedBits.is_nan(); |
48 | return expectedBits.is_nan() || |
49 | (actualBits.uintval() != expectedBits.uintval()); |
50 | } |
51 | |
52 | void explainError() override { |
53 | tlog << "Expected floating point value: " |
54 | << str(fputil::FPBits<T>(expected)) << '\n'; |
55 | tlog << "Actual floating point value: " << str(fputil::FPBits<T>(actual)) |
56 | << '\n'; |
57 | } |
58 | }; |
59 | |
60 | template <TestCond C, typename T> FPMatcher<T, C> getMatcher(T expectedValue) { |
61 | return FPMatcher<T, C>(expectedValue); |
62 | } |
63 | |
64 | template <typename T> struct FPTest : public Test { |
65 | using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>; |
66 | using StorageType = typename FPBits::StorageType; |
67 | static constexpr StorageType STORAGE_MAX = |
68 | LIBC_NAMESPACE::cpp::numeric_limits<StorageType>::max(); |
69 | static constexpr T zero = FPBits::zero(Sign::POS).get_val(); |
70 | static constexpr T neg_zero = FPBits::zero(Sign::NEG).get_val(); |
71 | static constexpr T aNaN = FPBits::quiet_nan().get_val(); |
72 | static constexpr T sNaN = FPBits::signaling_nan().get_val(); |
73 | static constexpr T inf = FPBits::inf(Sign::POS).get_val(); |
74 | static constexpr T neg_inf = FPBits::inf(Sign::NEG).get_val(); |
75 | static constexpr T min_normal = FPBits::min_normal().get_val(); |
76 | static constexpr T max_normal = FPBits::max_normal().get_val(); |
77 | static constexpr T min_denormal = FPBits::min_subnormal().get_val(); |
78 | static constexpr T max_denormal = FPBits::max_subnormal().get_val(); |
79 | |
80 | static constexpr int N_ROUNDING_MODES = 4; |
81 | static constexpr fputil::testing::RoundingMode ROUNDING_MODES[4] = { |
82 | fputil::testing::RoundingMode::Nearest, |
83 | fputil::testing::RoundingMode::Upward, |
84 | fputil::testing::RoundingMode::Downward, |
85 | fputil::testing::RoundingMode::TowardZero, |
86 | }; |
87 | }; |
88 | |
89 | } // namespace testing |
90 | } // namespace LIBC_NAMESPACE |
91 | |
92 | #define DECLARE_SPECIAL_CONSTANTS(T) \ |
93 | using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>; \ |
94 | using StorageType = typename FPBits::StorageType; \ |
95 | \ |
96 | static constexpr StorageType STORAGE_MAX = \ |
97 | LIBC_NAMESPACE::cpp::numeric_limits<StorageType>::max(); \ |
98 | const T zero = FPBits::zero(Sign::POS).get_val(); \ |
99 | const T neg_zero = FPBits::zero(Sign::NEG).get_val(); \ |
100 | const T aNaN = FPBits::quiet_nan().get_val(); \ |
101 | const T sNaN = FPBits::signaling_nan().get_val(); \ |
102 | const T inf = FPBits::inf(Sign::POS).get_val(); \ |
103 | const T neg_inf = FPBits::inf(Sign::NEG).get_val(); \ |
104 | const T min_normal = FPBits::min_normal().get_val(); \ |
105 | const T max_normal = FPBits::max_normal(Sign::POS).get_val(); \ |
106 | const T neg_max_normal = FPBits::max_normal(Sign::NEG).get_val(); \ |
107 | const T min_denormal = FPBits::min_subnormal(Sign::POS).get_val(); \ |
108 | const T neg_min_denormal = FPBits::min_subnormal(Sign::NEG).get_val(); \ |
109 | const T max_denormal = FPBits::max_subnormal().get_val(); \ |
110 | static constexpr int UNKNOWN_MATH_ROUNDING_DIRECTION = 99; \ |
111 | static constexpr LIBC_NAMESPACE::cpp::array<int, 6> \ |
112 | MATH_ROUNDING_DIRECTIONS_INCLUDING_UNKNOWN = { \ |
113 | FP_INT_UPWARD, FP_INT_DOWNWARD, \ |
114 | FP_INT_TOWARDZERO, FP_INT_TONEARESTFROMZERO, \ |
115 | FP_INT_TONEAREST, UNKNOWN_MATH_ROUNDING_DIRECTION, \ |
116 | }; |
117 | |
118 | #define EXPECT_FP_EQ(expected, actual) \ |
119 | EXPECT_THAT(actual, LIBC_NAMESPACE::testing::getMatcher< \ |
120 | LIBC_NAMESPACE::testing::TestCond::EQ>(expected)) |
121 | |
122 | #define TEST_FP_EQ(expected, actual) \ |
123 | LIBC_NAMESPACE::testing::getMatcher<LIBC_NAMESPACE::testing::TestCond::EQ>( \ |
124 | expected) \ |
125 | .match(actual) |
126 | |
127 | #define EXPECT_FP_IS_NAN(actual) EXPECT_TRUE((actual) != (actual)) |
128 | |
129 | #define ASSERT_FP_EQ(expected, actual) \ |
130 | ASSERT_THAT(actual, LIBC_NAMESPACE::testing::getMatcher< \ |
131 | LIBC_NAMESPACE::testing::TestCond::EQ>(expected)) |
132 | |
133 | #define EXPECT_FP_NE(expected, actual) \ |
134 | EXPECT_THAT(actual, LIBC_NAMESPACE::testing::getMatcher< \ |
135 | LIBC_NAMESPACE::testing::TestCond::NE>(expected)) |
136 | |
137 | #define ASSERT_FP_NE(expected, actual) \ |
138 | ASSERT_THAT(actual, LIBC_NAMESPACE::testing::getMatcher< \ |
139 | LIBC_NAMESPACE::testing::TestCond::NE>(expected)) |
140 | |
141 | #define EXPECT_MATH_ERRNO(expected) \ |
142 | do { \ |
143 | if (math_errhandling & MATH_ERRNO) { \ |
144 | int actual = LIBC_NAMESPACE::libc_errno; \ |
145 | LIBC_NAMESPACE::libc_errno = 0; \ |
146 | EXPECT_EQ(actual, expected); \ |
147 | } \ |
148 | } while (0) |
149 | |
150 | #define ASSERT_MATH_ERRNO(expected) \ |
151 | do { \ |
152 | if (math_errhandling & MATH_ERRNO) { \ |
153 | int actual = LIBC_NAMESPACE::libc_errno; \ |
154 | LIBC_NAMESPACE::libc_errno = 0; \ |
155 | ASSERT_EQ(actual, expected); \ |
156 | } \ |
157 | } while (0) |
158 | |
159 | #define EXPECT_FP_EXCEPTION(expected) \ |
160 | do { \ |
161 | if (math_errhandling & MATH_ERREXCEPT) { \ |
162 | EXPECT_GE(LIBC_NAMESPACE::fputil::test_except(FE_ALL_EXCEPT) & \ |
163 | (expected), \ |
164 | expected); \ |
165 | } \ |
166 | } while (0) |
167 | |
168 | #define ASSERT_FP_EXCEPTION(expected) \ |
169 | do { \ |
170 | if (math_errhandling & MATH_ERREXCEPT) { \ |
171 | ASSERT_GE(LIBC_NAMESPACE::fputil::test_except(FE_ALL_EXCEPT) & \ |
172 | (expected), \ |
173 | expected); \ |
174 | } \ |
175 | } while (0) |
176 | |
177 | #define EXPECT_FP_EQ_WITH_EXCEPTION(expected_val, actual_val, expected_except) \ |
178 | do { \ |
179 | LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT); \ |
180 | EXPECT_FP_EQ(expected_val, actual_val); \ |
181 | if (math_errhandling & MATH_ERREXCEPT) { \ |
182 | EXPECT_GE(LIBC_NAMESPACE::fputil::test_except(FE_ALL_EXCEPT) & \ |
183 | (expected_except), \ |
184 | expected_except); \ |
185 | LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT); \ |
186 | } \ |
187 | } while (0) |
188 | |
189 | #define EXPECT_FP_IS_NAN_WITH_EXCEPTION(actual_val, expected_except) \ |
190 | do { \ |
191 | LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT); \ |
192 | EXPECT_FP_IS_NAN(actual_val); \ |
193 | if (math_errhandling & MATH_ERREXCEPT) { \ |
194 | EXPECT_GE(LIBC_NAMESPACE::fputil::test_except(FE_ALL_EXCEPT) & \ |
195 | (expected_except), \ |
196 | expected_except); \ |
197 | LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT); \ |
198 | } \ |
199 | } while (0) |
200 | |
201 | #define EXPECT_FP_EQ_ALL_ROUNDING(expected, actual) \ |
202 | do { \ |
203 | using namespace LIBC_NAMESPACE::fputil::testing; \ |
204 | ForceRoundingMode __r1(RoundingMode::Nearest); \ |
205 | if (__r1.success) { \ |
206 | EXPECT_FP_EQ((expected), (actual)); \ |
207 | } \ |
208 | ForceRoundingMode __r2(RoundingMode::Upward); \ |
209 | if (__r2.success) { \ |
210 | EXPECT_FP_EQ((expected), (actual)); \ |
211 | } \ |
212 | ForceRoundingMode __r3(RoundingMode::Downward); \ |
213 | if (__r3.success) { \ |
214 | EXPECT_FP_EQ((expected), (actual)); \ |
215 | } \ |
216 | ForceRoundingMode __r4(RoundingMode::TowardZero); \ |
217 | if (__r4.success) { \ |
218 | EXPECT_FP_EQ((expected), (actual)); \ |
219 | } \ |
220 | } while (0) |
221 | |
222 | #endif // LLVM_LIBC_TEST_UNITTEST_FPMATCHER_H |
223 | |