• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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 
20 #include "hdr/math_macros.h"
21 #include <errno.h>
22 
23 static constexpr int ROUNDING_MODES[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO,
24                                           FE_TONEAREST};
25 
26 template <typename F, typename I, bool TestModes = false>
27 class RoundToIntegerTestTemplate
28     : public LIBC_NAMESPACE::testing::FEnvSafeTest {
29 public:
30   typedef I (*RoundToIntegerFunc)(F);
31 
32 private:
33   DECLARE_SPECIAL_CONSTANTS(F)
34 
35   static constexpr StorageType MAX_SUBNORMAL =
36       FPBits::max_subnormal().uintval();
37   static constexpr StorageType MIN_SUBNORMAL =
38       FPBits::min_subnormal().uintval();
39 
40   static constexpr I INTEGER_MIN = I(1) << (sizeof(I) * 8 - 1);
41   static constexpr I INTEGER_MAX = -(INTEGER_MIN + 1);
42 
test_one_input(RoundToIntegerFunc func,F input,I expected,bool expectError)43   void test_one_input(RoundToIntegerFunc func, F input, I expected,
44                       bool expectError) {
45     LIBC_NAMESPACE::libc_errno = 0;
46     LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
47 
48     ASSERT_EQ(func(input), expected);
49 
50     // TODO: Handle the !expectError case. It used to expect
51     // 0 for errno and exceptions, but this doesn't hold for
52     // all math functions using RoundToInteger test:
53     // https://github.com/llvm/llvm-project/pull/88816
54 #ifndef LIBC_TARGET_ARCH_IS_GPU
55     if (expectError) {
56       ASSERT_FP_EXCEPTION(FE_INVALID);
57       ASSERT_MATH_ERRNO(EDOM);
58     }
59 #endif
60   }
61 
62 public:
SetUp()63   void SetUp() override {
64     LIBC_NAMESPACE::testing::FEnvSafeTest::SetUp();
65 
66     if (math_errhandling & MATH_ERREXCEPT) {
67       // We will disable all exceptions so that the test will not
68       // crash with SIGFPE. We can still use fetestexcept to check
69       // if the appropriate flag was raised.
70       LIBC_NAMESPACE::fputil::disable_except(FE_ALL_EXCEPT);
71     }
72   }
73 
do_infinity_and_na_n_test(RoundToIntegerFunc func)74   void do_infinity_and_na_n_test(RoundToIntegerFunc func) {
75     test_one_input(func, inf, INTEGER_MAX, true);
76     test_one_input(func, neg_inf, INTEGER_MIN, true);
77     // This is currently never enabled, the
78     // LLVM_LIBC_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR CMake option in
79     // libc/CMakeLists.txt is not forwarded to C++.
80 #if LIBC_COPT_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR
81     // Result is not well-defined, we always returns INTEGER_MAX
82     test_one_input(func, aNaN, INTEGER_MAX, true);
83 #endif // LIBC_COPT_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR
84   }
85 
testInfinityAndNaN(RoundToIntegerFunc func)86   void testInfinityAndNaN(RoundToIntegerFunc func) {
87     if (TestModes) {
88       for (int mode : ROUNDING_MODES) {
89         LIBC_NAMESPACE::fputil::set_round(mode);
90         do_infinity_and_na_n_test(func);
91       }
92     } else {
93       do_infinity_and_na_n_test(func);
94     }
95   }
96 
do_round_numbers_test(RoundToIntegerFunc func)97   void do_round_numbers_test(RoundToIntegerFunc func) {
98     test_one_input(func, zero, I(0), false);
99     test_one_input(func, neg_zero, I(0), false);
100     test_one_input(func, F(1.0), I(1), false);
101     test_one_input(func, F(-1.0), I(-1), false);
102     test_one_input(func, F(10.0), I(10), false);
103     test_one_input(func, F(-10.0), I(-10), false);
104     test_one_input(func, F(1234.0), I(1234), false);
105     test_one_input(func, F(-1234.0), I(-1234), false);
106   }
107 
testRoundNumbers(RoundToIntegerFunc func)108   void testRoundNumbers(RoundToIntegerFunc func) {
109     if (TestModes) {
110       for (int mode : ROUNDING_MODES) {
111         LIBC_NAMESPACE::fputil::set_round(mode);
112         do_round_numbers_test(func);
113       }
114     } else {
115       do_round_numbers_test(func);
116     }
117   }
118 
testSubnormalRange(RoundToIntegerFunc func)119   void testSubnormalRange(RoundToIntegerFunc func) {
120     constexpr int COUNT = 1'000'001;
121     constexpr StorageType STEP = LIBC_NAMESPACE::cpp::max(
122         static_cast<StorageType>((MAX_SUBNORMAL - MIN_SUBNORMAL) / COUNT),
123         StorageType(1));
124     for (StorageType i = MIN_SUBNORMAL; i <= MAX_SUBNORMAL; i += STEP) {
125       F x = FPBits(i).get_val();
126       if (x == F(0.0))
127         continue;
128       // All subnormal numbers should round to zero.
129       if (TestModes) {
130         if (x > 0) {
131           LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);
132           test_one_input(func, x, I(1), false);
133           LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);
134           test_one_input(func, x, I(0), false);
135           LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);
136           test_one_input(func, x, I(0), false);
137           LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);
138           test_one_input(func, x, I(0), false);
139         } else {
140           LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);
141           test_one_input(func, x, I(0), false);
142           LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);
143           test_one_input(func, x, I(-1), false);
144           LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);
145           test_one_input(func, x, I(0), false);
146           LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);
147           test_one_input(func, x, I(0), false);
148         }
149       } else {
150         test_one_input(func, x, 0L, false);
151       }
152     }
153   }
154 };
155 
156 #define LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, TestModes)              \
157   using LlvmLibcRoundToIntegerTest =                                           \
158       RoundToIntegerTestTemplate<F, I, TestModes>;                             \
159   TEST_F(LlvmLibcRoundToIntegerTest, InfinityAndNaN) {                         \
160     testInfinityAndNaN(&func);                                                 \
161   }                                                                            \
162   TEST_F(LlvmLibcRoundToIntegerTest, RoundNumbers) {                           \
163     testRoundNumbers(&func);                                                   \
164   }                                                                            \
165   TEST_F(LlvmLibcRoundToIntegerTest, SubnormalRange) {                         \
166     testSubnormalRange(&func);                                                 \
167   }
168 
169 #define LIST_ROUND_TO_INTEGER_TESTS(F, I, func)                                \
170   LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, false)
171 
172 #define LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(F, I, func)                     \
173   LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, true)
174 
175 #endif // LLVM_LIBC_TEST_SRC_MATH_SMOKE_ROUNDTOINTEGERTEST_H
176