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