1//===-- Utility class to test different flavors of [l|ll]round --*- 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_SRC_MATH_SMOKE_ROUNDTOINTEGERTEST_H
10#define LLVM_LIBC_TEST_SRC_MATH_SMOKE_ROUNDTOINTEGERTEST_H
11
12#include "src/__support/CPP/algorithm.h"
13#include "src/__support/FPUtil/FEnvImpl.h"
14#include "src/__support/FPUtil/FPBits.h"
15#include "test/UnitTest/FEnvSafeTest.h"
16#include "test/UnitTest/FPMatcher.h"
17#include "test/UnitTest/Test.h"
18
19#include "hdr/math_macros.h"
20
21static constexpr int ROUNDING_MODES[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO,
22 FE_TONEAREST};
23
24template <typename F, typename I, bool TestModes = false>
25class RoundToIntegerTestTemplate
26 : public LIBC_NAMESPACE::testing::FEnvSafeTest {
27public:
28 typedef I (*RoundToIntegerFunc)(F);
29
30private:
31 DECLARE_SPECIAL_CONSTANTS(F)
32
33 static constexpr StorageType MAX_SUBNORMAL =
34 FPBits::max_subnormal().uintval();
35 static constexpr StorageType MIN_SUBNORMAL =
36 FPBits::min_subnormal().uintval();
37
38 static constexpr I INTEGER_MIN = I(1) << (sizeof(I) * 8 - 1);
39 static constexpr I INTEGER_MAX = -(INTEGER_MIN + 1);
40
41 void test_one_input(RoundToIntegerFunc func, F input, I expected,
42 bool expectError) {
43 libc_errno = 0;
44 LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
45
46 ASSERT_EQ(func(input), expected);
47
48 // TODO: Handle the !expectError case. It used to expect
49 // 0 for errno and exceptions, but this doesn't hold for
50 // all math functions using RoundToInteger test:
51 // https://github.com/llvm/llvm-project/pull/88816
52 if (expectError) {
53 ASSERT_FP_EXCEPTION(FE_INVALID);
54 ASSERT_MATH_ERRNO(EDOM);
55 }
56 }
57
58public:
59 void SetUp() override {
60 LIBC_NAMESPACE::testing::FEnvSafeTest::SetUp();
61
62 if (math_errhandling & MATH_ERREXCEPT) {
63 // We will disable all exceptions so that the test will not
64 // crash with SIGFPE. We can still use fetestexcept to check
65 // if the appropriate flag was raised.
66 LIBC_NAMESPACE::fputil::disable_except(FE_ALL_EXCEPT);
67 }
68 }
69
70 void do_infinity_and_na_n_test(RoundToIntegerFunc func) {
71 test_one_input(func, inf, INTEGER_MAX, true);
72 test_one_input(func, neg_inf, INTEGER_MIN, true);
73 // This is currently never enabled, the
74 // LLVM_LIBC_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR CMake option in
75 // libc/CMakeLists.txt is not forwarded to C++.
76#if LIBC_COPT_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR
77 // Result is not well-defined, we always returns INTEGER_MAX
78 test_one_input(func, aNaN, INTEGER_MAX, true);
79#endif // LIBC_COPT_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR
80 }
81
82 void testInfinityAndNaN(RoundToIntegerFunc func) {
83 if (TestModes) {
84 for (int mode : ROUNDING_MODES) {
85 LIBC_NAMESPACE::fputil::set_round(mode);
86 do_infinity_and_na_n_test(func);
87 }
88 } else {
89 do_infinity_and_na_n_test(func);
90 }
91 }
92
93 void do_round_numbers_test(RoundToIntegerFunc func) {
94 test_one_input(func, zero, I(0), false);
95 test_one_input(func, neg_zero, I(0), false);
96 test_one_input(func, F(1.0), I(1), false);
97 test_one_input(func, F(-1.0), I(-1), false);
98 test_one_input(func, F(10.0), I(10), false);
99 test_one_input(func, F(-10.0), I(-10), false);
100 test_one_input(func, F(1234.0), I(1234), false);
101 test_one_input(func, F(-1234.0), I(-1234), false);
102 }
103
104 void testRoundNumbers(RoundToIntegerFunc func) {
105 if (TestModes) {
106 for (int mode : ROUNDING_MODES) {
107 LIBC_NAMESPACE::fputil::set_round(mode);
108 do_round_numbers_test(func);
109 }
110 } else {
111 do_round_numbers_test(func);
112 }
113 }
114
115 void testSubnormalRange(RoundToIntegerFunc func) {
116 // Arbitrary, trades off completeness with testing time (esp. on failure)
117 constexpr int COUNT = 1'000;
118 constexpr StorageType STEP = LIBC_NAMESPACE::cpp::max(
119 static_cast<StorageType>((MAX_SUBNORMAL - MIN_SUBNORMAL) / COUNT),
120 StorageType(1));
121 for (StorageType i = MIN_SUBNORMAL; i <= MAX_SUBNORMAL; i += STEP) {
122 F x = FPBits(i).get_val();
123 if (x == F(0.0))
124 continue;
125 // All subnormal numbers should round to zero.
126 if (TestModes) {
127 if (x > 0) {
128 LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);
129 test_one_input(func, x, I(1), false);
130 LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);
131 test_one_input(func, x, I(0), false);
132 LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);
133 test_one_input(func, x, I(0), false);
134 LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);
135 test_one_input(func, x, I(0), false);
136 } else {
137 LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);
138 test_one_input(func, x, I(0), false);
139 LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);
140 test_one_input(func, x, I(-1), false);
141 LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);
142 test_one_input(func, x, I(0), false);
143 LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);
144 test_one_input(func, x, I(0), false);
145 }
146 } else {
147 test_one_input(func, x, 0L, false);
148 }
149 }
150 }
151};
152
153#define LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, TestModes) \
154 using LlvmLibcRoundToIntegerTest = \
155 RoundToIntegerTestTemplate<F, I, TestModes>; \
156 TEST_F(LlvmLibcRoundToIntegerTest, InfinityAndNaN) { \
157 testInfinityAndNaN(&func); \
158 } \
159 TEST_F(LlvmLibcRoundToIntegerTest, RoundNumbers) { \
160 testRoundNumbers(&func); \
161 } \
162 TEST_F(LlvmLibcRoundToIntegerTest, SubnormalRange) { \
163 testSubnormalRange(&func); \
164 }
165
166#define LIST_ROUND_TO_INTEGER_TESTS(F, I, func) \
167 LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, false)
168
169// The GPU target does not support different rounding modes.
170#ifdef LIBC_TARGET_ARCH_IS_GPU
171#define LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(F, I, func) \
172 LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, false)
173#else
174#define LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(F, I, func) \
175 LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, true)
176#endif
177
178#endif // LLVM_LIBC_TEST_SRC_MATH_SMOKE_ROUNDTOINTEGERTEST_H
179

source code of libc/test/src/math/smoke/RoundToIntegerTest.h