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