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/FPUtil/FEnvImpl.h" 13 #include "src/__support/FPUtil/FPBits.h" 14 #include "test/UnitTest/FEnvSafeTest.h" 15 #include "test/UnitTest/FPMatcher.h" 16 #include "test/UnitTest/Test.h" 17 #include "utils/MPFRWrapper/MPFRUtils.h" 18 19 #include "hdr/math_macros.h" 20 #include <errno.h> 21 22 namespace mpfr = LIBC_NAMESPACE::testing::mpfr; 23 24 static constexpr int ROUNDING_MODES[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, 25 FE_TONEAREST}; 26 27 template <typename F, typename I, bool TestModes = false> 28 class RoundToIntegerTestTemplate 29 : public LIBC_NAMESPACE::testing::FEnvSafeTest { 30 public: 31 typedef I (*RoundToIntegerFunc)(F); 32 33 private: 34 using FPBits = LIBC_NAMESPACE::fputil::FPBits<F>; 35 using StorageType = typename FPBits::StorageType; 36 37 const F zero = FPBits::zero().get_val(); 38 const F neg_zero = FPBits::zero(Sign::NEG).get_val(); 39 const F inf = FPBits::inf().get_val(); 40 const F neg_inf = FPBits::inf(Sign::NEG).get_val(); 41 const F nan = FPBits::quiet_nan().get_val(); 42 43 static constexpr StorageType MAX_NORMAL = FPBits::max_normal().uintval(); 44 static constexpr StorageType MIN_NORMAL = FPBits::min_normal().uintval(); 45 static constexpr StorageType MAX_SUBNORMAL = 46 FPBits::max_subnormal().uintval(); 47 static constexpr StorageType MIN_SUBNORMAL = 48 FPBits::min_subnormal().uintval(); 49 50 static constexpr I INTEGER_MIN = I(1) << (sizeof(I) * 8 - 1); 51 static constexpr I INTEGER_MAX = -(INTEGER_MIN + 1); 52 test_one_input(RoundToIntegerFunc func,F input,I expected,bool expectError)53 void test_one_input(RoundToIntegerFunc func, F input, I expected, 54 bool expectError) { 55 LIBC_NAMESPACE::libc_errno = 0; 56 LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT); 57 58 ASSERT_EQ(func(input), expected); 59 60 // TODO: Handle the !expectError case. It used to expect 61 // 0 for errno and exceptions, but this doesn't hold for 62 // all math functions using RoundToInteger test: 63 // https://github.com/llvm/llvm-project/pull/88816 64 if (expectError) { 65 ASSERT_FP_EXCEPTION(FE_INVALID); 66 ASSERT_MATH_ERRNO(EDOM); 67 } 68 } 69 to_mpfr_rounding_mode(int mode)70 static inline mpfr::RoundingMode to_mpfr_rounding_mode(int mode) { 71 switch (mode) { 72 case FE_UPWARD: 73 return mpfr::RoundingMode::Upward; 74 case FE_DOWNWARD: 75 return mpfr::RoundingMode::Downward; 76 case FE_TOWARDZERO: 77 return mpfr::RoundingMode::TowardZero; 78 case FE_TONEAREST: 79 return mpfr::RoundingMode::Nearest; 80 default: 81 __builtin_unreachable(); 82 } 83 } 84 85 public: SetUp()86 void SetUp() override { 87 LIBC_NAMESPACE::testing::FEnvSafeTest::SetUp(); 88 89 if (math_errhandling & MATH_ERREXCEPT) { 90 // We will disable all exceptions so that the test will not 91 // crash with SIGFPE. We can still use fetestexcept to check 92 // if the appropriate flag was raised. 93 LIBC_NAMESPACE::fputil::disable_except(FE_ALL_EXCEPT); 94 } 95 } 96 do_infinity_and_na_n_test(RoundToIntegerFunc func)97 void do_infinity_and_na_n_test(RoundToIntegerFunc func) { 98 test_one_input(func, inf, INTEGER_MAX, true); 99 test_one_input(func, neg_inf, INTEGER_MIN, true); 100 // This is currently never enabled, the 101 // LLVM_LIBC_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR CMake option in 102 // libc/CMakeLists.txt is not forwarded to C++. 103 #if LIBC_COPT_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR 104 // Result is not well-defined, we always returns INTEGER_MAX 105 test_one_input(func, nan, INTEGER_MAX, true); 106 #endif // LIBC_COPT_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR 107 } 108 testInfinityAndNaN(RoundToIntegerFunc func)109 void testInfinityAndNaN(RoundToIntegerFunc func) { 110 if (TestModes) { 111 for (int mode : ROUNDING_MODES) { 112 LIBC_NAMESPACE::fputil::set_round(mode); 113 do_infinity_and_na_n_test(func); 114 } 115 } else { 116 do_infinity_and_na_n_test(func); 117 } 118 } 119 do_round_numbers_test(RoundToIntegerFunc func)120 void do_round_numbers_test(RoundToIntegerFunc func) { 121 test_one_input(func, zero, I(0), false); 122 test_one_input(func, neg_zero, I(0), false); 123 test_one_input(func, F(1.0), I(1), false); 124 test_one_input(func, F(-1.0), I(-1), false); 125 test_one_input(func, F(10.0), I(10), false); 126 test_one_input(func, F(-10.0), I(-10), false); 127 test_one_input(func, F(1234.0), I(1234), false); 128 test_one_input(func, F(-1234.0), I(-1234), false); 129 130 // The rest of this function compares with an equivalent MPFR function 131 // which rounds floating point numbers to long values. There is no MPFR 132 // function to round to long long or wider integer values. So, we will 133 // the remaining tests only if the width of I less than equal to that of 134 // long. 135 if (sizeof(I) > sizeof(long)) 136 return; 137 138 constexpr int EXPONENT_LIMIT = sizeof(I) * 8 - 1; 139 // We start with 1.0 so that the implicit bit for x86 long doubles 140 // is set. 141 FPBits bits(F(1.0)); 142 bits.set_biased_exponent(EXPONENT_LIMIT + FPBits::EXP_BIAS); 143 bits.set_sign(Sign::NEG); 144 bits.set_mantissa(0); 145 146 F x = bits.get_val(); 147 long mpfr_result; 148 bool erangeflag = mpfr::round_to_long(x, mpfr_result); 149 ASSERT_FALSE(erangeflag); 150 test_one_input(func, x, mpfr_result, false); 151 } 152 testRoundNumbers(RoundToIntegerFunc func)153 void testRoundNumbers(RoundToIntegerFunc func) { 154 if (TestModes) { 155 for (int mode : ROUNDING_MODES) { 156 LIBC_NAMESPACE::fputil::set_round(mode); 157 do_round_numbers_test(func); 158 } 159 } else { 160 do_round_numbers_test(func); 161 } 162 } 163 do_fractions_test(RoundToIntegerFunc func,int mode)164 void do_fractions_test(RoundToIntegerFunc func, int mode) { 165 constexpr F FRACTIONS[] = {0.5, -0.5, 0.115, -0.115, 0.715, -0.715}; 166 for (F x : FRACTIONS) { 167 long mpfr_long_result; 168 bool erangeflag; 169 if (TestModes) 170 erangeflag = mpfr::round_to_long(x, to_mpfr_rounding_mode(mode), 171 mpfr_long_result); 172 else 173 erangeflag = mpfr::round_to_long(x, mpfr_long_result); 174 ASSERT_FALSE(erangeflag); 175 I mpfr_result = mpfr_long_result; 176 test_one_input(func, x, mpfr_result, false); 177 } 178 } 179 testFractions(RoundToIntegerFunc func)180 void testFractions(RoundToIntegerFunc func) { 181 if (TestModes) { 182 for (int mode : ROUNDING_MODES) { 183 LIBC_NAMESPACE::fputil::set_round(mode); 184 do_fractions_test(func, mode); 185 } 186 } else { 187 // Passing 0 for mode has no effect as it is not used in doFractionsTest 188 // when `TestModes` is false; 189 do_fractions_test(func, 0); 190 } 191 } 192 testIntegerOverflow(RoundToIntegerFunc func)193 void testIntegerOverflow(RoundToIntegerFunc func) { 194 // This function compares with an equivalent MPFR function which rounds 195 // floating point numbers to long values. There is no MPFR function to 196 // round to long long or wider integer values. So, we will peform the 197 // comparisons in this function only if the width of I less than equal to 198 // that of long. 199 if (sizeof(I) > sizeof(long)) 200 return; 201 202 constexpr int EXPONENT_LIMIT = sizeof(I) * 8 - 1; 203 // We start with 1.0 so that the implicit bit for x86 long doubles 204 // is set. 205 FPBits bits(F(1.0)); 206 bits.set_biased_exponent(EXPONENT_LIMIT + FPBits::EXP_BIAS); 207 bits.set_sign(Sign::NEG); 208 bits.set_mantissa(FPBits::FRACTION_MASK); 209 210 F x = bits.get_val(); 211 if (TestModes) { 212 for (int m : ROUNDING_MODES) { 213 LIBC_NAMESPACE::fputil::set_round(m); 214 long mpfr_long_result; 215 bool erangeflag = 216 mpfr::round_to_long(x, to_mpfr_rounding_mode(m), mpfr_long_result); 217 ASSERT_TRUE(erangeflag); 218 test_one_input(func, x, INTEGER_MIN, true); 219 } 220 } else { 221 long mpfr_long_result; 222 bool erangeflag = mpfr::round_to_long(x, mpfr_long_result); 223 ASSERT_TRUE(erangeflag); 224 test_one_input(func, x, INTEGER_MIN, true); 225 } 226 } 227 testSubnormalRange(RoundToIntegerFunc func)228 void testSubnormalRange(RoundToIntegerFunc func) { 229 constexpr StorageType COUNT = 1'000'001; 230 constexpr StorageType STEP = (MAX_SUBNORMAL - MIN_SUBNORMAL) / COUNT; 231 for (StorageType i = MIN_SUBNORMAL; i <= MAX_SUBNORMAL; i += STEP) { 232 F x = FPBits(i).get_val(); 233 if (x == F(0.0)) 234 continue; 235 // All subnormal numbers should round to zero. 236 if (TestModes) { 237 if (x > 0) { 238 LIBC_NAMESPACE::fputil::set_round(FE_UPWARD); 239 test_one_input(func, x, I(1), false); 240 LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD); 241 test_one_input(func, x, I(0), false); 242 LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO); 243 test_one_input(func, x, I(0), false); 244 LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST); 245 test_one_input(func, x, I(0), false); 246 } else { 247 LIBC_NAMESPACE::fputil::set_round(FE_UPWARD); 248 test_one_input(func, x, I(0), false); 249 LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD); 250 test_one_input(func, x, I(-1), false); 251 LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO); 252 test_one_input(func, x, I(0), false); 253 LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST); 254 test_one_input(func, x, I(0), false); 255 } 256 } else { 257 test_one_input(func, x, 0L, false); 258 } 259 } 260 } 261 testNormalRange(RoundToIntegerFunc func)262 void testNormalRange(RoundToIntegerFunc func) { 263 // This function compares with an equivalent MPFR function which rounds 264 // floating point numbers to long values. There is no MPFR function to 265 // round to long long or wider integer values. So, we will peform the 266 // comparisons in this function only if the width of I less than equal to 267 // that of long. 268 if (sizeof(I) > sizeof(long)) 269 return; 270 271 constexpr StorageType COUNT = 1'000'001; 272 constexpr StorageType STEP = (MAX_NORMAL - MIN_NORMAL) / COUNT; 273 for (StorageType i = MIN_NORMAL; i <= MAX_NORMAL; i += STEP) { 274 F x = FPBits(i).get_val(); 275 // In normal range on x86 platforms, the long double implicit 1 bit can be 276 // zero making the numbers NaN. We will skip them. 277 if (isnan(x)) { 278 continue; 279 } 280 281 if (TestModes) { 282 for (int m : ROUNDING_MODES) { 283 long mpfr_long_result; 284 bool erangeflag = mpfr::round_to_long(x, to_mpfr_rounding_mode(m), 285 mpfr_long_result); 286 I mpfr_result = mpfr_long_result; 287 LIBC_NAMESPACE::fputil::set_round(m); 288 if (erangeflag) 289 test_one_input(func, x, x > 0 ? INTEGER_MAX : INTEGER_MIN, true); 290 else 291 test_one_input(func, x, mpfr_result, false); 292 } 293 } else { 294 long mpfr_long_result; 295 bool erangeflag = mpfr::round_to_long(x, mpfr_long_result); 296 I mpfr_result = mpfr_long_result; 297 if (erangeflag) 298 test_one_input(func, x, x > 0 ? INTEGER_MAX : INTEGER_MIN, true); 299 else 300 test_one_input(func, x, mpfr_result, false); 301 } 302 } 303 } 304 }; 305 306 #define LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, TestModes) \ 307 using LlvmLibcRoundToIntegerTest = \ 308 RoundToIntegerTestTemplate<F, I, TestModes>; \ 309 TEST_F(LlvmLibcRoundToIntegerTest, InfinityAndNaN) { \ 310 testInfinityAndNaN(&func); \ 311 } \ 312 TEST_F(LlvmLibcRoundToIntegerTest, RoundNumbers) { \ 313 testRoundNumbers(&func); \ 314 } \ 315 TEST_F(LlvmLibcRoundToIntegerTest, Fractions) { testFractions(&func); } \ 316 TEST_F(LlvmLibcRoundToIntegerTest, IntegerOverflow) { \ 317 testIntegerOverflow(&func); \ 318 } \ 319 TEST_F(LlvmLibcRoundToIntegerTest, SubnormalRange) { \ 320 testSubnormalRange(&func); \ 321 } \ 322 TEST_F(LlvmLibcRoundToIntegerTest, NormalRange) { testNormalRange(&func); } 323 324 #define LIST_ROUND_TO_INTEGER_TESTS(F, I, func) \ 325 LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, false) 326 327 #define LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(F, I, func) \ 328 LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, true) 329 330 #endif // LLVM_LIBC_TEST_SRC_MATH_ROUNDTOINTEGERTEST_H 331