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

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