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_ROUNDTOINTEGERTEST_H |
10 | #define LLVM_LIBC_TEST_SRC_MATH_ROUNDTOINTEGERTEST_H |
11 | |
12 | #include "src/__support/FPUtil/FEnvImpl.h" |
13 | #include "src/__support/FPUtil/FPBits.h" |
14 | #include "test/UnitTest/FEnvSafeTest.h" |
15 | #include "test/UnitTest/FPMatcher.h" |
16 | #include "test/UnitTest/Test.h" |
17 | #include "utils/MPFRWrapper/MPFRUtils.h" |
18 | |
19 | #include "hdr/math_macros.h" |
20 | #include <errno.h> |
21 | |
22 | namespace mpfr = LIBC_NAMESPACE::testing::mpfr; |
23 | |
24 | static constexpr int ROUNDING_MODES[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, |
25 | FE_TONEAREST}; |
26 | |
27 | template <typename F, typename I, bool TestModes = false> |
28 | class RoundToIntegerTestTemplate |
29 | : public LIBC_NAMESPACE::testing::FEnvSafeTest { |
30 | public: |
31 | typedef I (*RoundToIntegerFunc)(F); |
32 | |
33 | private: |
34 | using FPBits = LIBC_NAMESPACE::fputil::FPBits<F>; |
35 | using StorageType = typename FPBits::StorageType; |
36 | |
37 | const F zero = FPBits::zero().get_val(); |
38 | const F neg_zero = FPBits::zero(Sign::NEG).get_val(); |
39 | const F inf = FPBits::inf().get_val(); |
40 | const F neg_inf = FPBits::inf(Sign::NEG).get_val(); |
41 | const F nan = FPBits::quiet_nan().get_val(); |
42 | |
43 | static constexpr StorageType MAX_NORMAL = FPBits::max_normal().uintval(); |
44 | static constexpr StorageType MIN_NORMAL = FPBits::min_normal().uintval(); |
45 | static constexpr StorageType MAX_SUBNORMAL = |
46 | FPBits::max_subnormal().uintval(); |
47 | static constexpr StorageType MIN_SUBNORMAL = |
48 | FPBits::min_subnormal().uintval(); |
49 | |
50 | static constexpr I INTEGER_MIN = I(1) << (sizeof(I) * 8 - 1); |
51 | static constexpr I INTEGER_MAX = -(INTEGER_MIN + 1); |
52 | |
53 | void test_one_input(RoundToIntegerFunc func, F input, I expected, |
54 | bool expectError) { |
55 | LIBC_NAMESPACE::libc_errno = 0; |
56 | LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT); |
57 | |
58 | ASSERT_EQ(func(input), expected); |
59 | |
60 | if (expectError) { |
61 | ASSERT_FP_EXCEPTION(FE_INVALID); |
62 | ASSERT_MATH_ERRNO(EDOM); |
63 | } else { |
64 | ASSERT_FP_EXCEPTION(0); |
65 | ASSERT_MATH_ERRNO(0); |
66 | } |
67 | } |
68 | |
69 | static inline mpfr::RoundingMode to_mpfr_rounding_mode(int mode) { |
70 | switch (mode) { |
71 | case FE_UPWARD: |
72 | return mpfr::RoundingMode::Upward; |
73 | case FE_DOWNWARD: |
74 | return mpfr::RoundingMode::Downward; |
75 | case FE_TOWARDZERO: |
76 | return mpfr::RoundingMode::TowardZero; |
77 | case FE_TONEAREST: |
78 | return mpfr::RoundingMode::Nearest; |
79 | default: |
80 | __builtin_unreachable(); |
81 | } |
82 | } |
83 | |
84 | public: |
85 | void SetUp() override { |
86 | LIBC_NAMESPACE::testing::FEnvSafeTest::SetUp(); |
87 | |
88 | if (math_errhandling & MATH_ERREXCEPT) { |
89 | // We will disable all exceptions so that the test will not |
90 | // crash with SIGFPE. We can still use fetestexcept to check |
91 | // if the appropriate flag was raised. |
92 | LIBC_NAMESPACE::fputil::disable_except(FE_ALL_EXCEPT); |
93 | } |
94 | } |
95 | |
96 | void do_infinity_and_na_n_test(RoundToIntegerFunc func) { |
97 | test_one_input(func, input: inf, expected: INTEGER_MAX, expectError: true); |
98 | test_one_input(func, input: neg_inf, expected: INTEGER_MIN, expectError: true); |
99 | // This is currently never enabled, the |
100 | // LLVM_LIBC_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR CMake option in |
101 | // libc/CMakeLists.txt is not forwarded to C++. |
102 | #if LIBC_COPT_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR |
103 | // Result is not well-defined, we always returns INTEGER_MAX |
104 | test_one_input(func, nan, INTEGER_MAX, true); |
105 | #endif // LIBC_COPT_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR |
106 | } |
107 | |
108 | void testInfinityAndNaN(RoundToIntegerFunc func) { |
109 | if (TestModes) { |
110 | for (int mode : ROUNDING_MODES) { |
111 | LIBC_NAMESPACE::fputil::set_round(mode); |
112 | do_infinity_and_na_n_test(func); |
113 | } |
114 | } else { |
115 | do_infinity_and_na_n_test(func); |
116 | } |
117 | } |
118 | |
119 | void do_round_numbers_test(RoundToIntegerFunc func) { |
120 | test_one_input(func, input: zero, expected: I(0), expectError: false); |
121 | test_one_input(func, input: neg_zero, expected: I(0), expectError: false); |
122 | test_one_input(func, input: F(1.0), expected: I(1), expectError: false); |
123 | test_one_input(func, input: F(-1.0), expected: I(-1), expectError: false); |
124 | test_one_input(func, input: F(10.0), expected: I(10), expectError: false); |
125 | test_one_input(func, input: F(-10.0), expected: I(-10), expectError: false); |
126 | test_one_input(func, input: F(1234.0), expected: I(1234), expectError: false); |
127 | test_one_input(func, input: F(-1234.0), expected: I(-1234), expectError: false); |
128 | |
129 | // The rest of this function compares with an equivalent MPFR function |
130 | // which rounds floating point numbers to long values. There is no MPFR |
131 | // function to round to long long or wider integer values. So, we will |
132 | // the remaining tests only if the width of I less than equal to that of |
133 | // long. |
134 | if (sizeof(I) > sizeof(long)) |
135 | return; |
136 | |
137 | constexpr int EXPONENT_LIMIT = sizeof(I) * 8 - 1; |
138 | // We start with 1.0 so that the implicit bit for x86 long doubles |
139 | // is set. |
140 | FPBits bits(F(1.0)); |
141 | bits.set_biased_exponent(EXPONENT_LIMIT + FPBits::EXP_BIAS); |
142 | bits.set_sign(Sign::NEG); |
143 | bits.set_mantissa(0); |
144 | |
145 | F x = bits.get_val(); |
146 | long mpfr_result; |
147 | bool erangeflag = mpfr::round_to_long(x, mpfr_result); |
148 | ASSERT_FALSE(erangeflag); |
149 | test_one_input(func, input: x, expected: mpfr_result, expectError: false); |
150 | } |
151 | |
152 | void testRoundNumbers(RoundToIntegerFunc func) { |
153 | if (TestModes) { |
154 | for (int mode : ROUNDING_MODES) { |
155 | LIBC_NAMESPACE::fputil::set_round(mode); |
156 | do_round_numbers_test(func); |
157 | } |
158 | } else { |
159 | do_round_numbers_test(func); |
160 | } |
161 | } |
162 | |
163 | void do_fractions_test(RoundToIntegerFunc func, int mode) { |
164 | constexpr F FRACTIONS[] = {0.5, -0.5, 0.115, -0.115, 0.715, -0.715}; |
165 | for (F x : FRACTIONS) { |
166 | long mpfr_long_result; |
167 | bool erangeflag; |
168 | if (TestModes) |
169 | erangeflag = mpfr::round_to_long(x, to_mpfr_rounding_mode(mode), |
170 | mpfr_long_result); |
171 | else |
172 | erangeflag = mpfr::round_to_long(x, mpfr_long_result); |
173 | ASSERT_FALSE(erangeflag); |
174 | I mpfr_result = mpfr_long_result; |
175 | test_one_input(func, input: x, expected: mpfr_result, expectError: false); |
176 | } |
177 | } |
178 | |
179 | void testFractions(RoundToIntegerFunc func) { |
180 | if (TestModes) { |
181 | for (int mode : ROUNDING_MODES) { |
182 | LIBC_NAMESPACE::fputil::set_round(mode); |
183 | do_fractions_test(func, mode); |
184 | } |
185 | } else { |
186 | // Passing 0 for mode has no effect as it is not used in doFractionsTest |
187 | // when `TestModes` is false; |
188 | do_fractions_test(func, mode: 0); |
189 | } |
190 | } |
191 | |
192 | void testIntegerOverflow(RoundToIntegerFunc func) { |
193 | // This function compares with an equivalent MPFR function which rounds |
194 | // floating point numbers to long values. There is no MPFR function to |
195 | // round to long long or wider integer values. So, we will peform the |
196 | // comparisons in this function only if the width of I less than equal to |
197 | // that of long. |
198 | if (sizeof(I) > sizeof(long)) |
199 | return; |
200 | |
201 | constexpr int EXPONENT_LIMIT = sizeof(I) * 8 - 1; |
202 | // We start with 1.0 so that the implicit bit for x86 long doubles |
203 | // is set. |
204 | FPBits bits(F(1.0)); |
205 | bits.set_biased_exponent(EXPONENT_LIMIT + FPBits::EXP_BIAS); |
206 | bits.set_sign(Sign::NEG); |
207 | bits.set_mantissa(FPBits::FRACTION_MASK); |
208 | |
209 | F x = bits.get_val(); |
210 | if (TestModes) { |
211 | for (int m : ROUNDING_MODES) { |
212 | LIBC_NAMESPACE::fputil::set_round(m); |
213 | long mpfr_long_result; |
214 | bool erangeflag = |
215 | mpfr::round_to_long(x, to_mpfr_rounding_mode(mode: m), mpfr_long_result); |
216 | ASSERT_TRUE(erangeflag); |
217 | test_one_input(func, input: x, expected: INTEGER_MIN, expectError: true); |
218 | } |
219 | } else { |
220 | long mpfr_long_result; |
221 | bool erangeflag = mpfr::round_to_long(x, mpfr_long_result); |
222 | ASSERT_TRUE(erangeflag); |
223 | test_one_input(func, input: x, expected: INTEGER_MIN, expectError: true); |
224 | } |
225 | } |
226 | |
227 | void testSubnormalRange(RoundToIntegerFunc func) { |
228 | constexpr StorageType COUNT = 1'000'001; |
229 | constexpr StorageType STEP = (MAX_SUBNORMAL - MIN_SUBNORMAL) / COUNT; |
230 | for (StorageType i = MIN_SUBNORMAL; i <= MAX_SUBNORMAL; i += STEP) { |
231 | F x = FPBits(i).get_val(); |
232 | if (x == F(0.0)) |
233 | continue; |
234 | // All subnormal numbers should round to zero. |
235 | if (TestModes) { |
236 | if (x > 0) { |
237 | LIBC_NAMESPACE::fputil::set_round(FE_UPWARD); |
238 | test_one_input(func, input: x, expected: I(1), expectError: false); |
239 | LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD); |
240 | test_one_input(func, input: x, expected: I(0), expectError: false); |
241 | LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO); |
242 | test_one_input(func, input: x, expected: I(0), expectError: false); |
243 | LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST); |
244 | test_one_input(func, input: x, expected: I(0), expectError: false); |
245 | } else { |
246 | LIBC_NAMESPACE::fputil::set_round(FE_UPWARD); |
247 | test_one_input(func, input: x, expected: I(0), expectError: false); |
248 | LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD); |
249 | test_one_input(func, input: x, expected: I(-1), expectError: false); |
250 | LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO); |
251 | test_one_input(func, input: x, expected: I(0), expectError: false); |
252 | LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST); |
253 | test_one_input(func, input: x, expected: I(0), expectError: false); |
254 | } |
255 | } else { |
256 | test_one_input(func, input: x, expected: 0L, expectError: false); |
257 | } |
258 | } |
259 | } |
260 | |
261 | void testNormalRange(RoundToIntegerFunc func) { |
262 | // This function compares with an equivalent MPFR function which rounds |
263 | // floating point numbers to long values. There is no MPFR function to |
264 | // round to long long or wider integer values. So, we will peform the |
265 | // comparisons in this function only if the width of I less than equal to |
266 | // that of long. |
267 | if (sizeof(I) > sizeof(long)) |
268 | return; |
269 | |
270 | constexpr StorageType COUNT = 1'000'001; |
271 | constexpr StorageType STEP = (MAX_NORMAL - MIN_NORMAL) / COUNT; |
272 | for (StorageType i = MIN_NORMAL; i <= MAX_NORMAL; i += STEP) { |
273 | F x = FPBits(i).get_val(); |
274 | // In normal range on x86 platforms, the long double implicit 1 bit can be |
275 | // zero making the numbers NaN. We will skip them. |
276 | if (isnan(x)) { |
277 | continue; |
278 | } |
279 | |
280 | if (TestModes) { |
281 | for (int m : ROUNDING_MODES) { |
282 | long mpfr_long_result; |
283 | bool erangeflag = mpfr::round_to_long(x, to_mpfr_rounding_mode(mode: m), |
284 | mpfr_long_result); |
285 | I mpfr_result = mpfr_long_result; |
286 | LIBC_NAMESPACE::fputil::set_round(m); |
287 | if (erangeflag) |
288 | test_one_input(func, input: x, expected: x > 0 ? INTEGER_MAX : INTEGER_MIN, expectError: true); |
289 | else |
290 | test_one_input(func, input: x, expected: mpfr_result, expectError: false); |
291 | } |
292 | } else { |
293 | long mpfr_long_result; |
294 | bool erangeflag = mpfr::round_to_long(x, mpfr_long_result); |
295 | I mpfr_result = mpfr_long_result; |
296 | if (erangeflag) |
297 | test_one_input(func, input: x, expected: x > 0 ? INTEGER_MAX : INTEGER_MIN, expectError: true); |
298 | else |
299 | test_one_input(func, input: x, expected: mpfr_result, expectError: false); |
300 | } |
301 | } |
302 | } |
303 | }; |
304 | |
305 | #define LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, TestModes) \ |
306 | using LlvmLibcRoundToIntegerTest = \ |
307 | RoundToIntegerTestTemplate<F, I, TestModes>; \ |
308 | TEST_F(LlvmLibcRoundToIntegerTest, InfinityAndNaN) { \ |
309 | testInfinityAndNaN(&func); \ |
310 | } \ |
311 | TEST_F(LlvmLibcRoundToIntegerTest, RoundNumbers) { \ |
312 | testRoundNumbers(&func); \ |
313 | } \ |
314 | TEST_F(LlvmLibcRoundToIntegerTest, Fractions) { testFractions(&func); } \ |
315 | TEST_F(LlvmLibcRoundToIntegerTest, IntegerOverflow) { \ |
316 | testIntegerOverflow(&func); \ |
317 | } \ |
318 | TEST_F(LlvmLibcRoundToIntegerTest, SubnormalRange) { \ |
319 | testSubnormalRange(&func); \ |
320 | } \ |
321 | TEST_F(LlvmLibcRoundToIntegerTest, NormalRange) { testNormalRange(&func); } |
322 | |
323 | #define LIST_ROUND_TO_INTEGER_TESTS(F, I, func) \ |
324 | LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, false) |
325 | |
326 | #define LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(F, I, func) \ |
327 | LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, true) |
328 | |
329 | #endif // LLVM_LIBC_TEST_SRC_MATH_ROUNDTOINTEGERTEST_H |
330 | |