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/errno/llvmlibc_errno.h" 13 #include "utils/FPUtil/FPBits.h" 14 #include "utils/MPFRWrapper/MPFRUtils.h" 15 #include "utils/UnitTest/Test.h" 16 17 #include <math.h> 18 #if math_errhandling & MATH_ERRNO 19 #include <errno.h> 20 #endif 21 #if math_errhandling & MATH_ERREXCEPT 22 #include "utils/FPUtil/FEnv.h" 23 #endif 24 25 namespace mpfr = __llvm_libc::testing::mpfr; 26 27 static constexpr int roundingModes[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, 28 FE_TONEAREST}; 29 30 template <typename F, typename I, bool TestModes = false> 31 class RoundToIntegerTestTemplate : public __llvm_libc::testing::Test { 32 public: 33 typedef I (*RoundToIntegerFunc)(F); 34 35 private: 36 using FPBits = __llvm_libc::fputil::FPBits<F>; 37 using UIntType = typename FPBits::UIntType; 38 39 const F zero = __llvm_libc::fputil::FPBits<F>::zero(); 40 const F negZero = __llvm_libc::fputil::FPBits<F>::negZero(); 41 const F inf = __llvm_libc::fputil::FPBits<F>::inf(); 42 const F negInf = __llvm_libc::fputil::FPBits<F>::negInf(); 43 const F nan = __llvm_libc::fputil::FPBits<F>::buildNaN(1); 44 static constexpr I IntegerMin = I(1) << (sizeof(I) * 8 - 1); 45 static constexpr I IntegerMax = -(IntegerMin + 1); 46 47 void testOneInput(RoundToIntegerFunc func, F input, I expected, 48 bool expectError) { 49 #if math_errhandling & MATH_ERRNO 50 llvmlibc_errno = 0; 51 #endif 52 #if math_errhandling & MATH_ERREXCEPT 53 __llvm_libc::fputil::clearExcept(FE_ALL_EXCEPT); 54 #endif 55 56 ASSERT_EQ(func(input), expected); 57 58 if (expectError) { 59 #if math_errhandling & MATH_ERREXCEPT 60 ASSERT_EQ(__llvm_libc::fputil::testExcept(FE_ALL_EXCEPT), FE_INVALID); 61 #endif 62 #if math_errhandling & MATH_ERRNO 63 ASSERT_EQ(llvmlibc_errno, EDOM); 64 #endif 65 } else { 66 #if math_errhandling & MATH_ERREXCEPT 67 ASSERT_EQ(__llvm_libc::fputil::testExcept(FE_ALL_EXCEPT), 0); 68 #endif 69 #if math_errhandling & MATH_ERRNO 70 ASSERT_EQ(llvmlibc_errno, 0); 71 #endif 72 } 73 } 74 75 static inline mpfr::RoundingMode toMPFRRoundingMode(int mode) { 76 switch (mode) { 77 case FE_UPWARD: 78 return mpfr::RoundingMode::Upward; 79 case FE_DOWNWARD: 80 return mpfr::RoundingMode::Downward; 81 case FE_TOWARDZERO: 82 return mpfr::RoundingMode::TowardZero; 83 case FE_TONEAREST: 84 return mpfr::RoundingMode::Nearest; 85 default: 86 __builtin_unreachable(); 87 } 88 } 89 90 public: 91 void SetUp() override { 92 #if math_errhandling & MATH_ERREXCEPT 93 // We will disable all exceptions so that the test will not 94 // crash with SIGFPE. We can still use fetestexcept to check 95 // if the appropriate flag was raised. 96 __llvm_libc::fputil::disableExcept(FE_ALL_EXCEPT); 97 #endif 98 } 99 100 void doInfinityAndNaNTest(RoundToIntegerFunc func) { 101 testOneInput(func, inf, IntegerMax, true); 102 testOneInput(func, negInf, IntegerMin, true); 103 testOneInput(func, nan, IntegerMax, true); 104 } 105 106 void testInfinityAndNaN(RoundToIntegerFunc func) { 107 if (TestModes) { 108 for (int mode : roundingModes) { 109 __llvm_libc::fputil::setRound(mode); 110 doInfinityAndNaNTest(func); 111 } 112 } else { 113 doInfinityAndNaNTest(func); 114 } 115 } 116 117 void doRoundNumbersTest(RoundToIntegerFunc func) { 118 testOneInput(func, zero, I(0), false); 119 testOneInput(func, negZero, I(0), false); 120 testOneInput(func, F(1.0), I(1), false); 121 testOneInput(func, F(-1.0), I(-1), false); 122 testOneInput(func, F(10.0), I(10), false); 123 testOneInput(func, F(-10.0), I(-10), false); 124 testOneInput(func, F(1234.0), I(1234), false); 125 testOneInput(func, F(-1234.0), I(-1234), false); 126 127 // The rest of this this function compares with an equivalent MPFR function 128 // which rounds floating point numbers to long values. There is no MPFR 129 // function to round to long long or wider integer values. So, we will 130 // the remaining tests only if the width of I less than equal to that of 131 // long. 132 if (sizeof(I) > sizeof(long)) 133 return; 134 135 constexpr int exponentLimit = sizeof(I) * 8 - 1; 136 // We start with 1.0 so that the implicit bit for x86 long doubles 137 // is set. 138 FPBits bits(F(1.0)); 139 bits.exponent = exponentLimit + FPBits::exponentBias; 140 bits.sign = 1; 141 bits.mantissa = 0; 142 143 F x = bits; 144 long mpfrResult; 145 bool erangeflag = mpfr::RoundToLong(x, mpfrResult); 146 ASSERT_FALSE(erangeflag); 147 testOneInput(func, x, mpfrResult, false); 148 } 149 150 void testRoundNumbers(RoundToIntegerFunc func) { 151 if (TestModes) { 152 for (int mode : roundingModes) { 153 __llvm_libc::fputil::setRound(mode); 154 doRoundNumbersTest(func); 155 } 156 } else { 157 doRoundNumbersTest(func); 158 } 159 } 160 161 void doFractionsTest(RoundToIntegerFunc func, int mode) { 162 constexpr F fractions[] = {0.5, -0.5, 0.115, -0.115, 0.715, -0.715}; 163 for (F x : fractions) { 164 long mpfrLongResult; 165 bool erangeflag; 166 if (TestModes) 167 erangeflag = 168 mpfr::RoundToLong(x, toMPFRRoundingMode(mode), mpfrLongResult); 169 else 170 erangeflag = mpfr::RoundToLong(x, mpfrLongResult); 171 ASSERT_FALSE(erangeflag); 172 I mpfrResult = mpfrLongResult; 173 testOneInput(func, x, mpfrResult, false); 174 } 175 } 176 177 void testFractions(RoundToIntegerFunc func) { 178 if (TestModes) { 179 for (int mode : roundingModes) { 180 __llvm_libc::fputil::setRound(mode); 181 doFractionsTest(func, mode); 182 } 183 } else { 184 // Passing 0 for mode has no effect as it is not used in doFractionsTest 185 // when `TestModes` is false; 186 doFractionsTest(func, 0); 187 } 188 } 189 190 void testIntegerOverflow(RoundToIntegerFunc func) { 191 // This function compares with an equivalent MPFR function which rounds 192 // floating point numbers to long values. There is no MPFR function to 193 // round to long long or wider integer values. So, we will peform the 194 // comparisons in this function only if the width of I less than equal to 195 // that of long. 196 if (sizeof(I) > sizeof(long)) 197 return; 198 199 constexpr int exponentLimit = sizeof(I) * 8 - 1; 200 // We start with 1.0 so that the implicit bit for x86 long doubles 201 // is set. 202 FPBits bits(F(1.0)); 203 bits.exponent = exponentLimit + FPBits::exponentBias; 204 bits.sign = 1; 205 bits.mantissa = UIntType(0x1) 206 << (__llvm_libc::fputil::MantissaWidth<F>::value - 1); 207 208 F x = bits; 209 if (TestModes) { 210 for (int m : roundingModes) { 211 __llvm_libc::fputil::setRound(m); 212 long mpfrLongResult; 213 bool erangeflag = 214 mpfr::RoundToLong(x, toMPFRRoundingMode(m), mpfrLongResult); 215 ASSERT_TRUE(erangeflag); 216 testOneInput(func, x, IntegerMin, true); 217 } 218 } else { 219 long mpfrLongResult; 220 bool erangeflag = mpfr::RoundToLong(x, mpfrLongResult); 221 ASSERT_TRUE(erangeflag); 222 testOneInput(func, x, IntegerMin, true); 223 } 224 } 225 226 void testSubnormalRange(RoundToIntegerFunc func) { 227 constexpr UIntType count = 1000001; 228 constexpr UIntType step = 229 (FPBits::maxSubnormal - FPBits::minSubnormal) / count; 230 for (UIntType i = FPBits::minSubnormal; i <= FPBits::maxSubnormal; 231 i += step) { 232 F x = FPBits(i); 233 if (x == F(0.0)) 234 continue; 235 // All subnormal numbers should round to zero. 236 if (TestModes) { 237 if (x > 0) { 238 __llvm_libc::fputil::setRound(FE_UPWARD); 239 testOneInput(func, x, I(1), false); 240 __llvm_libc::fputil::setRound(FE_DOWNWARD); 241 testOneInput(func, x, I(0), false); 242 __llvm_libc::fputil::setRound(FE_TOWARDZERO); 243 testOneInput(func, x, I(0), false); 244 __llvm_libc::fputil::setRound(FE_TONEAREST); 245 testOneInput(func, x, I(0), false); 246 } else { 247 __llvm_libc::fputil::setRound(FE_UPWARD); 248 testOneInput(func, x, I(0), false); 249 __llvm_libc::fputil::setRound(FE_DOWNWARD); 250 testOneInput(func, x, I(-1), false); 251 __llvm_libc::fputil::setRound(FE_TOWARDZERO); 252 testOneInput(func, x, I(0), false); 253 __llvm_libc::fputil::setRound(FE_TONEAREST); 254 testOneInput(func, x, I(0), false); 255 } 256 } else { 257 testOneInput(func, x, 0L, false); 258 } 259 } 260 } 261 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 UIntType count = 1000001; 272 constexpr UIntType step = (FPBits::maxNormal - FPBits::minNormal) / count; 273 for (UIntType i = FPBits::minNormal; i <= FPBits::maxNormal; i += step) { 274 F x = FPBits(i); 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 : roundingModes) { 283 long mpfrLongResult; 284 bool erangeflag = 285 mpfr::RoundToLong(x, toMPFRRoundingMode(m), mpfrLongResult); 286 I mpfrResult = mpfrLongResult; 287 __llvm_libc::fputil::setRound(m); 288 if (erangeflag) 289 testOneInput(func, x, x > 0 ? IntegerMax : IntegerMin, true); 290 else 291 testOneInput(func, x, mpfrResult, false); 292 } 293 } else { 294 long mpfrLongResult; 295 bool erangeflag = mpfr::RoundToLong(x, mpfrLongResult); 296 I mpfrResult = mpfrLongResult; 297 if (erangeflag) 298 testOneInput(func, x, x > 0 ? IntegerMax : IntegerMin, true); 299 else 300 testOneInput(func, x, mpfrResult, 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