xref: /aosp_15_r20/external/llvm-libc/test/src/math/smoke/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_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 
21 static constexpr int ROUNDING_MODES[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO,
22                                           FE_TONEAREST};
23 
24 template <typename F, typename I, bool TestModes = false>
25 class RoundToIntegerTestTemplate
26     : public LIBC_NAMESPACE::testing::FEnvSafeTest {
27 public:
28   typedef I (*RoundToIntegerFunc)(F);
29 
30 private:
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 
test_one_input(RoundToIntegerFunc func,F input,I expected,bool expectError)41   void test_one_input(RoundToIntegerFunc func, F input, I expected,
42                       bool expectError) {
43     LIBC_NAMESPACE::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 
58 public:
SetUp()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 
do_infinity_and_na_n_test(RoundToIntegerFunc func)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 
testInfinityAndNaN(RoundToIntegerFunc func)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 
do_round_numbers_test(RoundToIntegerFunc func)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 
testRoundNumbers(RoundToIntegerFunc func)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 
testSubnormalRange(RoundToIntegerFunc func)115   void testSubnormalRange(RoundToIntegerFunc func) {
116     constexpr int COUNT = 1'000'001;
117     constexpr StorageType STEP = LIBC_NAMESPACE::cpp::max(
118         static_cast<StorageType>((MAX_SUBNORMAL - MIN_SUBNORMAL) / COUNT),
119         StorageType(1));
120     for (StorageType i = MIN_SUBNORMAL; i <= MAX_SUBNORMAL; i += STEP) {
121       F x = FPBits(i).get_val();
122       if (x == F(0.0))
123         continue;
124       // All subnormal numbers should round to zero.
125       if (TestModes) {
126         if (x > 0) {
127           LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);
128           test_one_input(func, x, I(1), false);
129           LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);
130           test_one_input(func, x, I(0), false);
131           LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);
132           test_one_input(func, x, I(0), false);
133           LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);
134           test_one_input(func, x, I(0), false);
135         } else {
136           LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);
137           test_one_input(func, x, I(0), false);
138           LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);
139           test_one_input(func, x, I(-1), false);
140           LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);
141           test_one_input(func, x, I(0), false);
142           LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);
143           test_one_input(func, x, I(0), false);
144         }
145       } else {
146         test_one_input(func, x, 0L, false);
147       }
148     }
149   }
150 };
151 
152 #define LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, TestModes)              \
153   using LlvmLibcRoundToIntegerTest =                                           \
154       RoundToIntegerTestTemplate<F, I, TestModes>;                             \
155   TEST_F(LlvmLibcRoundToIntegerTest, InfinityAndNaN) {                         \
156     testInfinityAndNaN(&func);                                                 \
157   }                                                                            \
158   TEST_F(LlvmLibcRoundToIntegerTest, RoundNumbers) {                           \
159     testRoundNumbers(&func);                                                   \
160   }                                                                            \
161   TEST_F(LlvmLibcRoundToIntegerTest, SubnormalRange) {                         \
162     testSubnormalRange(&func);                                                 \
163   }
164 
165 #define LIST_ROUND_TO_INTEGER_TESTS(F, I, func)                                \
166   LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, false)
167 
168 // The GPU target does not support different rounding modes.
169 #ifdef LIBC_TARGET_ARCH_IS_GPU
170 #define LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(F, I, func)                     \
171   LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, false)
172 #else
173 #define LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(F, I, func)                     \
174   LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, true)
175 #endif
176 
177 #endif // LLVM_LIBC_TEST_SRC_MATH_SMOKE_ROUNDTOINTEGERTEST_H
178