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
22namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
23
24static constexpr int ROUNDING_MODES[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO,
25 FE_TONEAREST};
26
27template <typename F, typename I, bool TestModes = false>
28class RoundToIntegerTestTemplate
29 : public LIBC_NAMESPACE::testing::FEnvSafeTest {
30public:
31 typedef I (*RoundToIntegerFunc)(F);
32
33private:
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
84public:
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

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