Warning: This file is not a C or C++ file. It does not have highlighting.

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 "src/__support/macros/config.h"
18#include "src/__support/macros/properties/architectures.h"
19#include "test/UnitTest/RoundingModeUtils.h"
20#include "test/UnitTest/StringUtils.h"
21#include "test/UnitTest/Test.h"
22
23#include "hdr/math_macros.h"
24
25using LIBC_NAMESPACE::Sign;
26
27namespace LIBC_NAMESPACE_DECL {
28namespace testing {
29
30template <typename T, TestCond Condition> class FPMatcher : public Matcher<T> {
31 static_assert(cpp::is_floating_point_v<T>,
32 "FPMatcher can only be used with floating point values.");
33 static_assert(Condition == TestCond::EQ || Condition == TestCond::NE,
34 "Unsupported FPMatcher test condition.");
35
36 T expected;
37 T actual;
38
39public:
40 FPMatcher(T expectedValue) : expected(expectedValue) {}
41
42 bool match(T actualValue) {
43 actual = actualValue;
44 fputil::FPBits<T> actualBits(actual), expectedBits(expected);
45 if (Condition == TestCond::EQ)
46 return (actualBits.is_nan() && expectedBits.is_nan()) ||
47 (actualBits.uintval() == expectedBits.uintval());
48
49 // If condition == TestCond::NE.
50 if (actualBits.is_nan())
51 return !expectedBits.is_nan();
52 return expectedBits.is_nan() ||
53 (actualBits.uintval() != expectedBits.uintval());
54 }
55
56 void explainError() override {
57 tlog << "Expected floating point value: "
58 << str(fputil::FPBits<T>(expected)) << '\n';
59 tlog << "Actual floating point value: " << str(fputil::FPBits<T>(actual))
60 << '\n';
61 }
62};
63
64template <typename T, TestCond Condition> class CFPMatcher : public Matcher<T> {
65 static_assert(
66 cpp::is_complex_v<T>,
67 "CFPMatcher can only be used with complex floating point values.");
68 static_assert(Condition == TestCond::EQ || Condition == TestCond::NE,
69 "Unsupported CFPMatcher test condition.");
70
71 T expected;
72 T actual;
73
74public:
75 CFPMatcher(T expectedValue) : expected(expectedValue) {}
76
77 template <typename CFT> bool matchComplex() {
78 CFT *actualCmplxPtr = reinterpret_cast<CFT *>(&actual);
79 CFT *expectedCmplxPtr = reinterpret_cast<CFT *>(&expected);
80 CFT actualReal = actualCmplxPtr[0];
81 CFT actualImag = actualCmplxPtr[1];
82 CFT expectedReal = expectedCmplxPtr[0];
83 CFT expectedImag = expectedCmplxPtr[1];
84 fputil::FPBits<CFT> actualRealBits(actualReal),
85 expectedRealBits(expectedReal);
86 fputil::FPBits<CFT> actualImagBits(actualImag),
87 expectedImagBits(expectedImag);
88 if (Condition == TestCond::EQ)
89 return ((actualRealBits.is_nan() && expectedRealBits.is_nan()) ||
90 (actualRealBits.uintval() == expectedRealBits.uintval())) &&
91 ((actualImagBits.is_nan() && expectedImagBits.is_nan()) ||
92 (actualImagBits.uintval() == expectedImagBits.uintval()));
93
94 // If condition == TestCond::NE.
95 if (actualRealBits.is_nan() && expectedRealBits.is_nan())
96 return !expectedRealBits.is_nan() && !expectedImagBits.is_nan();
97 if (actualRealBits.is_nan())
98 return !expectedRealBits.is_nan();
99 if (actualImagBits.is_nan())
100 return !expectedImagBits.is_nan();
101 return (expectedRealBits.is_nan() ||
102 actualRealBits.uintval() != expectedRealBits.uintval()) &&
103 (expectedImagBits.is_nan() ||
104 actualImagBits.uintval() != expectedImagBits.uintval());
105 }
106
107 template <typename CFT> void explainErrorComplex() {
108 CFT *actualCmplxPtr = reinterpret_cast<CFT *>(&actual);
109 CFT *expectedCmplxPtr = reinterpret_cast<CFT *>(&expected);
110 CFT actualReal = actualCmplxPtr[0];
111 CFT actualImag = actualCmplxPtr[1];
112 CFT expectedReal = expectedCmplxPtr[0];
113 CFT expectedImag = expectedCmplxPtr[1];
114 tlog << "Expected complex floating point value: "
115 << str(fputil::FPBits<CFT>(expectedReal)) + " + " +
116 str(fputil::FPBits<CFT>(expectedImag)) + "i"
117 << '\n';
118 tlog << "Actual complex floating point value: "
119 << str(fputil::FPBits<CFT>(actualReal)) + " + " +
120 str(fputil::FPBits<CFT>(actualImag)) + "i"
121 << '\n';
122 }
123
124 bool match(T actualValue) {
125 actual = actualValue;
126 if constexpr (cpp::is_complex_type_same<T, _Complex float>())
127 return matchComplex<float>();
128 else if constexpr (cpp::is_complex_type_same<T, _Complex double>())
129 return matchComplex<double>();
130 else if constexpr (cpp::is_complex_type_same<T, _Complex long double>())
131 return matchComplex<long double>();
132#ifdef LIBC_TYPES_HAS_CFLOAT16
133 else if constexpr (cpp::is_complex_type_same<T, cfloat16>())
134 return matchComplex<float16>();
135#endif
136#ifdef LIBC_TYPES_HAS_CFLOAT128
137 else if constexpr (cpp::is_complex_type_same<T, cfloat128>())
138 return matchComplex<float128>();
139#endif
140 }
141
142 void explainError() override {
143 if constexpr (cpp::is_complex_type_same<T, _Complex float>())
144 return explainErrorComplex<float>();
145 else if constexpr (cpp::is_complex_type_same<T, _Complex double>())
146 return explainErrorComplex<double>();
147 else if constexpr (cpp::is_complex_type_same<T, _Complex long double>())
148 return explainErrorComplex<long double>();
149#ifdef LIBC_TYPES_HAS_CFLOAT16
150 else if constexpr (cpp::is_complex_type_same<T, cfloat16>())
151 return explainErrorComplex<float16>();
152#endif
153#ifdef LIBC_TYPES_HAS_CFLOAT128
154 else if constexpr (cpp::is_complex_type_same<T, cfloat128>())
155 return explainErrorComplex<float128>();
156#endif
157 }
158};
159
160template <TestCond C, typename T> FPMatcher<T, C> getMatcher(T expectedValue) {
161 return FPMatcher<T, C>(expectedValue);
162}
163
164template <TestCond C, typename T>
165CFPMatcher<T, C> getMatcherComplex(T expectedValue) {
166 return CFPMatcher<T, C>(expectedValue);
167}
168
169template <typename T> struct FPTest : public Test {
170 using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>;
171 using StorageType = typename FPBits::StorageType;
172 static constexpr StorageType STORAGE_MAX =
173 LIBC_NAMESPACE::cpp::numeric_limits<StorageType>::max();
174 static constexpr T zero = FPBits::zero(Sign::POS).get_val();
175 static constexpr T neg_zero = FPBits::zero(Sign::NEG).get_val();
176 static constexpr T aNaN = FPBits::quiet_nan(Sign::POS).get_val();
177 static constexpr T neg_aNaN = FPBits::quiet_nan(Sign::NEG).get_val();
178 static constexpr T sNaN = FPBits::signaling_nan().get_val();
179 static constexpr T inf = FPBits::inf(Sign::POS).get_val();
180 static constexpr T neg_inf = FPBits::inf(Sign::NEG).get_val();
181 static constexpr T min_normal = FPBits::min_normal().get_val();
182 static constexpr T max_normal = FPBits::max_normal(Sign::POS).get_val();
183 static constexpr T neg_max_normal = FPBits::max_normal(Sign::NEG).get_val();
184 static constexpr T min_denormal = FPBits::min_subnormal().get_val();
185 static constexpr T max_denormal = FPBits::max_subnormal().get_val();
186
187 static constexpr int N_ROUNDING_MODES = 4;
188 static constexpr fputil::testing::RoundingMode ROUNDING_MODES[4] = {
189 fputil::testing::RoundingMode::Nearest,
190 fputil::testing::RoundingMode::Upward,
191 fputil::testing::RoundingMode::Downward,
192 fputil::testing::RoundingMode::TowardZero,
193 };
194};
195
196// Add facility to test Flush-Denormal-To-Zero (FTZ) and Denormal-As-Zero (DAZ)
197// modes.
198// These tests to ensure that our implementations will not crash under these
199// modes.
200#if defined(LIBC_TARGET_ARCH_IS_X86_64) && __has_builtin(__builtin_ia32_stmxcsr)
201
202#define LIBC_TEST_FTZ_DAZ
203
204static constexpr unsigned FTZ = 0x8000; // Flush denormal to zero
205static constexpr unsigned DAZ = 0x0040; // Denormal as zero
206
207struct ModifyMXCSR {
208 ModifyMXCSR(unsigned flags) {
209 old_mxcsr = __builtin_ia32_stmxcsr();
210 __builtin_ia32_ldmxcsr(old_mxcsr | flags);
211 }
212
213 ~ModifyMXCSR() { __builtin_ia32_ldmxcsr(old_mxcsr); }
214
215private:
216 unsigned old_mxcsr;
217};
218
219#endif
220
221} // namespace testing
222} // namespace LIBC_NAMESPACE_DECL
223
224#define DECLARE_SPECIAL_CONSTANTS(T) \
225 using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>; \
226 using StorageType = typename FPBits::StorageType; \
227 \
228 static constexpr StorageType STORAGE_MAX = \
229 LIBC_NAMESPACE::cpp::numeric_limits<StorageType>::max(); \
230 const T zero = FPBits::zero(Sign::POS).get_val(); \
231 const T neg_zero = FPBits::zero(Sign::NEG).get_val(); \
232 const T aNaN = FPBits::quiet_nan(Sign::POS).get_val(); \
233 const T neg_aNaN = FPBits::quiet_nan(Sign::NEG).get_val(); \
234 const T sNaN = FPBits::signaling_nan(Sign::POS).get_val(); \
235 const T neg_sNaN = FPBits::signaling_nan(Sign::NEG).get_val(); \
236 const T inf = FPBits::inf(Sign::POS).get_val(); \
237 const T neg_inf = FPBits::inf(Sign::NEG).get_val(); \
238 const T min_normal = FPBits::min_normal().get_val(); \
239 const T max_normal = FPBits::max_normal(Sign::POS).get_val(); \
240 const T neg_max_normal = FPBits::max_normal(Sign::NEG).get_val(); \
241 const T min_denormal = FPBits::min_subnormal(Sign::POS).get_val(); \
242 const T neg_min_denormal = FPBits::min_subnormal(Sign::NEG).get_val(); \
243 const T max_denormal = FPBits::max_subnormal().get_val(); \
244 static constexpr int UNKNOWN_MATH_ROUNDING_DIRECTION = 99; \
245 static constexpr LIBC_NAMESPACE::cpp::array<int, 6> \
246 MATH_ROUNDING_DIRECTIONS_INCLUDING_UNKNOWN = { \
247 FP_INT_UPWARD, FP_INT_DOWNWARD, \
248 FP_INT_TOWARDZERO, FP_INT_TONEARESTFROMZERO, \
249 FP_INT_TONEAREST, UNKNOWN_MATH_ROUNDING_DIRECTION, \
250 };
251
252#define EXPECT_FP_EQ(expected, actual) \
253 EXPECT_THAT(actual, LIBC_NAMESPACE::testing::getMatcher< \
254 LIBC_NAMESPACE::testing::TestCond::EQ>(expected))
255
256#define EXPECT_CFP_EQ(expected, actual) \
257 EXPECT_THAT(actual, LIBC_NAMESPACE::testing::getMatcherComplex< \
258 LIBC_NAMESPACE::testing::TestCond::EQ>(expected))
259
260#define TEST_FP_EQ(expected, actual) \
261 LIBC_NAMESPACE::testing::getMatcher<LIBC_NAMESPACE::testing::TestCond::EQ>( \
262 expected) \
263 .match(actual)
264
265#define EXPECT_FP_IS_NAN(actual) EXPECT_TRUE((actual) != (actual))
266
267#define ASSERT_FP_EQ(expected, actual) \
268 ASSERT_THAT(actual, LIBC_NAMESPACE::testing::getMatcher< \
269 LIBC_NAMESPACE::testing::TestCond::EQ>(expected))
270
271#define EXPECT_FP_NE(expected, actual) \
272 EXPECT_THAT(actual, LIBC_NAMESPACE::testing::getMatcher< \
273 LIBC_NAMESPACE::testing::TestCond::NE>(expected))
274
275#define ASSERT_FP_NE(expected, actual) \
276 ASSERT_THAT(actual, LIBC_NAMESPACE::testing::getMatcher< \
277 LIBC_NAMESPACE::testing::TestCond::NE>(expected))
278
279#define EXPECT_MATH_ERRNO(expected) \
280 do { \
281 if (math_errhandling & MATH_ERRNO) { \
282 int actual = libc_errno; \
283 libc_errno = 0; \
284 EXPECT_EQ(actual, expected); \
285 } \
286 } while (0)
287
288#define ASSERT_MATH_ERRNO(expected) \
289 do { \
290 if (math_errhandling & MATH_ERRNO) { \
291 int actual = libc_errno; \
292 libc_errno = 0; \
293 ASSERT_EQ(actual, expected); \
294 } \
295 } while (0)
296
297#define EXPECT_FP_EXCEPTION(expected) \
298 do { \
299 if (math_errhandling & MATH_ERREXCEPT) { \
300 EXPECT_EQ( \
301 LIBC_NAMESPACE::fputil::test_except( \
302 static_cast<int>(FE_ALL_EXCEPT)) & \
303 ((expected) ? (expected) : static_cast<int>(FE_ALL_EXCEPT)), \
304 (expected)); \
305 } \
306 } while (0)
307
308#define ASSERT_FP_EXCEPTION(expected) \
309 do { \
310 if (math_errhandling & MATH_ERREXCEPT) { \
311 ASSERT_EQ( \
312 LIBC_NAMESPACE::fputil::test_except( \
313 static_cast<int>(FE_ALL_EXCEPT)) & \
314 ((expected) ? (expected) : static_cast<int>(FE_ALL_EXCEPT)), \
315 (expected)); \
316 } \
317 } while (0)
318
319#define EXPECT_FP_EQ_WITH_EXCEPTION(expected_val, actual_val, expected_except) \
320 do { \
321 LIBC_NAMESPACE::fputil::clear_except(static_cast<int>(FE_ALL_EXCEPT)); \
322 EXPECT_FP_EQ(expected_val, actual_val); \
323 EXPECT_FP_EXCEPTION(expected_except); \
324 } while (0)
325
326#define EXPECT_FP_IS_NAN_WITH_EXCEPTION(actual_val, expected_except) \
327 do { \
328 LIBC_NAMESPACE::fputil::clear_except(static_cast<int>(FE_ALL_EXCEPT)); \
329 EXPECT_FP_IS_NAN(actual_val); \
330 EXPECT_FP_EXCEPTION(expected_except); \
331 } while (0)
332
333#define EXPECT_FP_EQ_ROUNDING_MODE(expected, actual, rounding_mode) \
334 do { \
335 using namespace LIBC_NAMESPACE::fputil::testing; \
336 ForceRoundingMode __r((rounding_mode)); \
337 if (__r.success) { \
338 EXPECT_FP_EQ((expected), (actual)); \
339 } \
340 } while (0)
341
342#define EXPECT_FP_EQ_ROUNDING_NEAREST(expected, actual) \
343 EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Nearest)
344
345#define EXPECT_FP_EQ_ROUNDING_UPWARD(expected, actual) \
346 EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Upward)
347
348#define EXPECT_FP_EQ_ROUNDING_DOWNWARD(expected, actual) \
349 EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Downward)
350
351#define EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(expected, actual) \
352 EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::TowardZero)
353
354#define EXPECT_FP_EQ_ALL_ROUNDING_1(expected, actual) \
355 do { \
356 EXPECT_FP_EQ_ROUNDING_NEAREST((expected), (actual)); \
357 EXPECT_FP_EQ_ROUNDING_UPWARD((expected), (actual)); \
358 EXPECT_FP_EQ_ROUNDING_DOWNWARD((expected), (actual)); \
359 EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO((expected), (actual)); \
360 } while (0)
361
362#define EXPECT_FP_EQ_ALL_ROUNDING_4(expected_nearest, expected_upward, \
363 expected_downward, expected_toward_zero, \
364 actual) \
365 do { \
366 EXPECT_FP_EQ_ROUNDING_NEAREST((expected_nearest), (actual)); \
367 EXPECT_FP_EQ_ROUNDING_UPWARD((expected_upward), (actual)); \
368 EXPECT_FP_EQ_ROUNDING_DOWNWARD((expected_downward), (actual)); \
369 EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO((expected_toward_zero), (actual)); \
370 } while (0)
371
372#define EXPECT_FP_EQ_ALL_ROUNDING_UNSUPPORTED(...) \
373 static_assert(false, "Unsupported number of arguments")
374
375#define EXPECT_FP_EQ_ALL_ROUNDING_GET_6TH_ARG(ARG1, ARG2, ARG3, ARG4, ARG5, \
376 ARG6, ...) \
377 ARG6
378
379#define EXPECT_FP_EQ_ALL_ROUNDING_SELECTION(...) \
380 EXPECT_FP_EQ_ALL_ROUNDING_GET_6TH_ARG( \
381 __VA_ARGS__, EXPECT_FP_EQ_ALL_ROUNDING_4, \
382 EXPECT_FP_EQ_ALL_ROUNDING_UNSUPPORTED, \
383 EXPECT_FP_EQ_ALL_ROUNDING_UNSUPPORTED, EXPECT_FP_EQ_ALL_ROUNDING_1)
384
385#define EXPECT_FP_EQ_ALL_ROUNDING(...) \
386 EXPECT_FP_EQ_ALL_ROUNDING_SELECTION(__VA_ARGS__)(__VA_ARGS__)
387
388#define ASSERT_FP_EQ_ROUNDING_MODE(expected, actual, rounding_mode) \
389 do { \
390 using namespace LIBC_NAMESPACE::fputil::testing; \
391 ForceRoundingMode __r((rounding_mode)); \
392 if (__r.success) { \
393 ASSERT_FP_EQ((expected), (actual)); \
394 } \
395 } while (0)
396
397#define ASSERT_FP_EQ_ROUNDING_NEAREST(expected, actual) \
398 ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Nearest)
399
400#define ASSERT_FP_EQ_ROUNDING_UPWARD(expected, actual) \
401 ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Upward)
402
403#define ASSERT_FP_EQ_ROUNDING_DOWNWARD(expected, actual) \
404 ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Downward)
405
406#define ASSERT_FP_EQ_ROUNDING_TOWARD_ZERO(expected, actual) \
407 ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::TowardZero)
408
409#define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \
410 expected, actual, expected_except, rounding_mode) \
411 do { \
412 using namespace LIBC_NAMESPACE::fputil::testing; \
413 ForceRoundingMode __r((rounding_mode)); \
414 if (__r.success) { \
415 LIBC_NAMESPACE::fputil::clear_except(static_cast<int>(FE_ALL_EXCEPT)); \
416 EXPECT_FP_EQ((expected), (actual)); \
417 EXPECT_FP_EXCEPTION(expected_except); \
418 } \
419 } while (0)
420
421#define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_NEAREST(expected, actual, \
422 expected_except) \
423 EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \
424 (expected), (actual), (expected_except), RoundingMode::Nearest)
425
426#define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD(expected, actual, \
427 expected_except) \
428 EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \
429 (expected), (actual), (expected_except), RoundingMode::Upward)
430
431#define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_DOWNWARD(expected, actual, \
432 expected_except) \
433 EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \
434 (expected), (actual), (expected_except), RoundingMode::Downward)
435
436#define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_TOWARD_ZERO(expected, actual, \
437 expected_except) \
438 EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \
439 (expected), (actual), (expected_except), RoundingMode::TowardZero)
440
441#define EXPECT_FP_EQ_WITH_EXCEPTION_ALL_ROUNDING(expected, actual, \
442 expected_except) \
443 do { \
444 EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_NEAREST((expected), (actual), \
445 (expected_except)); \
446 EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD((expected), (actual), \
447 (expected_except)); \
448 EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_DOWNWARD((expected), (actual), \
449 (expected_except)); \
450 EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_TOWARD_ZERO((expected), (actual), \
451 (expected_except)); \
452 } while (0)
453
454#endif // LLVM_LIBC_TEST_UNITTEST_FPMATCHER_H
455

Warning: This file is not a C or C++ file. It does not have highlighting.

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of libc/test/UnitTest/FPMatcher.h