xref: /aosp_15_r20/external/llvm-libc/test/src/math/RoundToIntegerTest.h (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
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 
23 namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
24 using LIBC_NAMESPACE::Sign;
25 
26 static constexpr int ROUNDING_MODES[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO,
27                                           FE_TONEAREST};
28 
29 template <typename FloatType, typename IntType, bool TestModes = false>
30 class RoundToIntegerTestTemplate
31     : public LIBC_NAMESPACE::testing::FEnvSafeTest {
32 public:
33   typedef IntType (*RoundToIntegerFunc)(FloatType);
34 
35 private:
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 
test_one_input(RoundToIntegerFunc func,FloatType input,IntType expected,bool expectError)56   void test_one_input(RoundToIntegerFunc func, FloatType input,
57                       IntType expected, bool expectError) {
58     LIBC_NAMESPACE::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 
to_mpfr_rounding_mode(int mode)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 
88 public:
SetUp()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 
do_infinity_and_na_n_test(RoundToIntegerFunc func)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 
testInfinityAndNaN(RoundToIntegerFunc func)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 
do_round_numbers_test(RoundToIntegerFunc func)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, FloatType(1.0), IntType(1), false);
127     test_one_input(func, FloatType(-1.0), IntType(-1), false);
128     test_one_input(func, FloatType(10.0), IntType(10), false);
129     test_one_input(func, FloatType(-10.0), IntType(-10), false);
130     test_one_input(func, FloatType(1234.0), IntType(1234), false);
131     test_one_input(func, FloatType(-1234.0), IntType(-1234), 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, x, mpfr_result, false);
157   }
158 
testRoundNumbers(RoundToIntegerFunc func)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 
do_fractions_test(RoundToIntegerFunc func,int mode)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, x, mpfr_result, false);
186     }
187   }
188 
testFractions(RoundToIntegerFunc func)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);
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, 0);
199     }
200   }
201 
testIntegerOverflow(RoundToIntegerFunc func)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, x, INTEGER_MIN, 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, x, INTEGER_MIN, true);
237     }
238   }
239 
testSubnormalRange(RoundToIntegerFunc func)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, x, IntType(1), false);
254           LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);
255           test_one_input(func, x, IntType(0), false);
256           LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);
257           test_one_input(func, x, IntType(0), false);
258           LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);
259           test_one_input(func, x, IntType(0), false);
260         } else {
261           LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);
262           test_one_input(func, x, IntType(0), false);
263           LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);
264           test_one_input(func, x, IntType(-1), false);
265           LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);
266           test_one_input(func, x, IntType(0), false);
267           LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);
268           test_one_input(func, x, IntType(0), false);
269         }
270       } else {
271         test_one_input(func, x, 0L, false);
272       }
273     }
274   }
275 
testNormalRange(RoundToIntegerFunc func)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, x, x > 0 ? INTEGER_MAX : INTEGER_MIN, true);
306           else
307             test_one_input(func, x, mpfr_result, 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, x, x > 0 ? INTEGER_MAX : INTEGER_MIN, true);
315         else
316           test_one_input(func, x, mpfr_result, 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