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/FPBits.h" 13 #include "utils/MPFRWrapper/MPFRUtils.h" 14 #include "utils/UnitTest/Test.h" 15 16 #include <math.h> 17 #if math_errhandling & MATH_ERRNO 18 #include <errno.h> 19 #endif 20 #if math_errhandling & MATH_ERREXCEPT 21 #include "src/__support/FPUtil/FEnvUtils.h" 22 #endif 23 24 namespace mpfr = __llvm_libc::testing::mpfr; 25 26 static constexpr int roundingModes[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, 27 FE_TONEAREST}; 28 29 template <typename F, typename I, bool TestModes = false> 30 class RoundToIntegerTestTemplate : public __llvm_libc::testing::Test { 31 public: 32 typedef I (*RoundToIntegerFunc)(F); 33 34 private: 35 using FPBits = __llvm_libc::fputil::FPBits<F>; 36 using UIntType = typename FPBits::UIntType; 37 38 const F zero = F(__llvm_libc::fputil::FPBits<F>::zero()); 39 const F neg_zero = F(__llvm_libc::fputil::FPBits<F>::neg_zero()); 40 const F inf = F(__llvm_libc::fputil::FPBits<F>::inf()); 41 const F neg_inf = F(__llvm_libc::fputil::FPBits<F>::neg_inf()); 42 const F nan = F(__llvm_libc::fputil::FPBits<F>::build_nan(1)); 43 static constexpr I IntegerMin = I(1) << (sizeof(I) * 8 - 1); 44 static constexpr I IntegerMax = -(IntegerMin + 1); 45 46 void testOneInput(RoundToIntegerFunc func, F input, I expected, 47 bool expectError) { 48 #if math_errhandling & MATH_ERRNO 49 errno = 0; 50 #endif 51 #if math_errhandling & MATH_ERREXCEPT 52 __llvm_libc::fputil::clear_except(FE_ALL_EXCEPT); 53 #endif 54 55 ASSERT_EQ(func(input), expected); 56 57 if (expectError) { 58 #if math_errhandling & MATH_ERREXCEPT 59 ASSERT_EQ(__llvm_libc::fputil::test_except(FE_ALL_EXCEPT), FE_INVALID); 60 #endif 61 #if math_errhandling & MATH_ERRNO 62 ASSERT_EQ(errno, EDOM); 63 #endif 64 } else { 65 #if math_errhandling & MATH_ERREXCEPT 66 ASSERT_EQ(__llvm_libc::fputil::test_except(FE_ALL_EXCEPT), 0); 67 #endif 68 #if math_errhandling & MATH_ERRNO 69 ASSERT_EQ(errno, 0); 70 #endif 71 } 72 } 73 74 static inline mpfr::RoundingMode toMPFRRoundingMode(int mode) { 75 switch (mode) { 76 case FE_UPWARD: 77 return mpfr::RoundingMode::Upward; 78 case FE_DOWNWARD: 79 return mpfr::RoundingMode::Downward; 80 case FE_TOWARDZERO: 81 return mpfr::RoundingMode::TowardZero; 82 case FE_TONEAREST: 83 return mpfr::RoundingMode::Nearest; 84 default: 85 __builtin_unreachable(); 86 } 87 } 88 89 public: 90 void SetUp() override { 91 #if math_errhandling & MATH_ERREXCEPT 92 // We will disable all exceptions so that the test will not 93 // crash with SIGFPE. We can still use fetestexcept to check 94 // if the appropriate flag was raised. 95 __llvm_libc::fputil::disable_except(FE_ALL_EXCEPT); 96 #endif 97 } 98 99 void doInfinityAndNaNTest(RoundToIntegerFunc func) { 100 testOneInput(func, inf, IntegerMax, true); 101 testOneInput(func, neg_inf, IntegerMin, true); 102 testOneInput(func, nan, IntegerMax, true); 103 } 104 105 void testInfinityAndNaN(RoundToIntegerFunc func) { 106 if (TestModes) { 107 for (int mode : roundingModes) { 108 __llvm_libc::fputil::set_round(mode); 109 doInfinityAndNaNTest(func); 110 } 111 } else { 112 doInfinityAndNaNTest(func); 113 } 114 } 115 116 void doRoundNumbersTest(RoundToIntegerFunc func) { 117 testOneInput(func, zero, I(0), false); 118 testOneInput(func, neg_zero, I(0), false); 119 testOneInput(func, F(1.0), I(1), false); 120 testOneInput(func, F(-1.0), I(-1), false); 121 testOneInput(func, F(10.0), I(10), false); 122 testOneInput(func, F(-10.0), I(-10), false); 123 testOneInput(func, F(1234.0), I(1234), false); 124 testOneInput(func, F(-1234.0), I(-1234), false); 125 126 // The rest of this 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 exponentLimit = 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_unbiased_exponent(exponentLimit + FPBits::EXPONENT_BIAS); 139 bits.set_sign(1); 140 bits.set_mantissa(0); 141 142 F x = F(bits); 143 long mpfrResult; 144 bool erangeflag = mpfr::RoundToLong(x, mpfrResult); 145 ASSERT_FALSE(erangeflag); 146 testOneInput(func, x, mpfrResult, false); 147 } 148 149 void testRoundNumbers(RoundToIntegerFunc func) { 150 if (TestModes) { 151 for (int mode : roundingModes) { 152 __llvm_libc::fputil::set_round(mode); 153 doRoundNumbersTest(func); 154 } 155 } else { 156 doRoundNumbersTest(func); 157 } 158 } 159 160 void doFractionsTest(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 mpfrLongResult; 164 bool erangeflag; 165 if (TestModes) 166 erangeflag = 167 mpfr::RoundToLong(x, toMPFRRoundingMode(mode), mpfrLongResult); 168 else 169 erangeflag = mpfr::RoundToLong(x, mpfrLongResult); 170 ASSERT_FALSE(erangeflag); 171 I mpfrResult = mpfrLongResult; 172 testOneInput(func, x, mpfrResult, false); 173 } 174 } 175 176 void testFractions(RoundToIntegerFunc func) { 177 if (TestModes) { 178 for (int mode : roundingModes) { 179 __llvm_libc::fputil::set_round(mode); 180 doFractionsTest(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 doFractionsTest(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 exponentLimit = 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_unbiased_exponent(exponentLimit + FPBits::EXPONENT_BIAS); 203 bits.set_sign(1); 204 bits.set_mantissa(UIntType(0x1) 205 << (__llvm_libc::fputil::MantissaWidth<F>::VALUE - 1)); 206 207 F x = F(bits); 208 if (TestModes) { 209 for (int m : roundingModes) { 210 __llvm_libc::fputil::set_round(m); 211 long mpfrLongResult; 212 bool erangeflag = 213 mpfr::RoundToLong(x, toMPFRRoundingMode(m), mpfrLongResult); 214 ASSERT_TRUE(erangeflag); 215 testOneInput(func, x, IntegerMin, true); 216 } 217 } else { 218 long mpfrLongResult; 219 bool erangeflag = mpfr::RoundToLong(x, mpfrLongResult); 220 ASSERT_TRUE(erangeflag); 221 testOneInput(func, x, IntegerMin, true); 222 } 223 } 224 225 void testSubnormalRange(RoundToIntegerFunc func) { 226 constexpr UIntType count = 1000001; 227 constexpr UIntType step = 228 (FPBits::MAX_SUBNORMAL - FPBits::MIN_SUBNORMAL) / count; 229 for (UIntType i = FPBits::MIN_SUBNORMAL; i <= FPBits::MAX_SUBNORMAL; 230 i += step) { 231 F x = F(FPBits(i)); 232 if (x == F(0.0)) 233 continue; 234 // All subnormal numbers should round to zero. 235 if (TestModes) { 236 if (x > 0) { 237 __llvm_libc::fputil::set_round(FE_UPWARD); 238 testOneInput(func, x, I(1), false); 239 __llvm_libc::fputil::set_round(FE_DOWNWARD); 240 testOneInput(func, x, I(0), false); 241 __llvm_libc::fputil::set_round(FE_TOWARDZERO); 242 testOneInput(func, x, I(0), false); 243 __llvm_libc::fputil::set_round(FE_TONEAREST); 244 testOneInput(func, x, I(0), false); 245 } else { 246 __llvm_libc::fputil::set_round(FE_UPWARD); 247 testOneInput(func, x, I(0), false); 248 __llvm_libc::fputil::set_round(FE_DOWNWARD); 249 testOneInput(func, x, I(-1), false); 250 __llvm_libc::fputil::set_round(FE_TOWARDZERO); 251 testOneInput(func, x, I(0), false); 252 __llvm_libc::fputil::set_round(FE_TONEAREST); 253 testOneInput(func, x, I(0), false); 254 } 255 } else { 256 testOneInput(func, x, 0L, false); 257 } 258 } 259 } 260 261 void testNormalRange(RoundToIntegerFunc func) { 262 // This function compares with an equivalent MPFR function which rounds 263 // floating point numbers to long values. There is no MPFR function to 264 // round to long long or wider integer values. So, we will peform the 265 // comparisons in this function only if the width of I less than equal to 266 // that of long. 267 if (sizeof(I) > sizeof(long)) 268 return; 269 270 constexpr UIntType count = 1000001; 271 constexpr UIntType step = (FPBits::MAX_NORMAL - FPBits::MIN_NORMAL) / count; 272 for (UIntType i = FPBits::MIN_NORMAL; i <= FPBits::MAX_NORMAL; i += step) { 273 F x = F(FPBits(i)); 274 // In normal range on x86 platforms, the long double implicit 1 bit can be 275 // zero making the numbers NaN. We will skip them. 276 if (isnan(x)) { 277 continue; 278 } 279 280 if (TestModes) { 281 for (int m : roundingModes) { 282 long mpfrLongResult; 283 bool erangeflag = 284 mpfr::RoundToLong(x, toMPFRRoundingMode(m), mpfrLongResult); 285 I mpfrResult = mpfrLongResult; 286 __llvm_libc::fputil::set_round(m); 287 if (erangeflag) 288 testOneInput(func, x, x > 0 ? IntegerMax : IntegerMin, true); 289 else 290 testOneInput(func, x, mpfrResult, false); 291 } 292 } else { 293 long mpfrLongResult; 294 bool erangeflag = mpfr::RoundToLong(x, mpfrLongResult); 295 I mpfrResult = mpfrLongResult; 296 if (erangeflag) 297 testOneInput(func, x, x > 0 ? IntegerMax : IntegerMin, true); 298 else 299 testOneInput(func, x, mpfrResult, false); 300 } 301 } 302 } 303 }; 304 305 #define LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, TestModes) \ 306 using LlvmLibcRoundToIntegerTest = \ 307 RoundToIntegerTestTemplate<F, I, TestModes>; \ 308 TEST_F(LlvmLibcRoundToIntegerTest, InfinityAndNaN) { \ 309 testInfinityAndNaN(&func); \ 310 } \ 311 TEST_F(LlvmLibcRoundToIntegerTest, RoundNumbers) { \ 312 testRoundNumbers(&func); \ 313 } \ 314 TEST_F(LlvmLibcRoundToIntegerTest, Fractions) { testFractions(&func); } \ 315 TEST_F(LlvmLibcRoundToIntegerTest, IntegerOverflow) { \ 316 testIntegerOverflow(&func); \ 317 } \ 318 TEST_F(LlvmLibcRoundToIntegerTest, SubnormalRange) { \ 319 testSubnormalRange(&func); \ 320 } \ 321 TEST_F(LlvmLibcRoundToIntegerTest, NormalRange) { testNormalRange(&func); } 322 323 #define LIST_ROUND_TO_INTEGER_TESTS(F, I, func) \ 324 LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, false) 325 326 #define LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(F, I, func) \ 327 LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, true) 328 329 #endif // LLVM_LIBC_TEST_SRC_MATH_ROUNDTOINTEGERTEST_H 330