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