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 negZero = F(__llvm_libc::fputil::FPBits<F>::negZero()); 40 const F inf = F(__llvm_libc::fputil::FPBits<F>::inf()); 41 const F negInf = F(__llvm_libc::fputil::FPBits<F>::negInf()); 42 const F nan = F(__llvm_libc::fputil::FPBits<F>::buildNaN(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::clearExcept(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::testExcept(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::testExcept(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::disableExcept(FE_ALL_EXCEPT); 96 #endif 97 } 98 99 void doInfinityAndNaNTest(RoundToIntegerFunc func) { 100 testOneInput(func, inf, IntegerMax, true); 101 testOneInput(func, negInf, 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::setRound(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, negZero, 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.setUnbiasedExponent(exponentLimit + FPBits::exponentBias); 139 bits.setSign(1); 140 bits.setMantissa(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::setRound(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::setRound(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.setUnbiasedExponent(exponentLimit + FPBits::exponentBias); 203 bits.setSign(1); 204 bits.setMantissa(UIntType(0x1) << (__llvm_libc::fputil::MantissaWidth<F>::value - 1)); 205 206 F x = F(bits); 207 if (TestModes) { 208 for (int m : roundingModes) { 209 __llvm_libc::fputil::setRound(m); 210 long mpfrLongResult; 211 bool erangeflag = 212 mpfr::RoundToLong(x, toMPFRRoundingMode(m), mpfrLongResult); 213 ASSERT_TRUE(erangeflag); 214 testOneInput(func, x, IntegerMin, true); 215 } 216 } else { 217 long mpfrLongResult; 218 bool erangeflag = mpfr::RoundToLong(x, mpfrLongResult); 219 ASSERT_TRUE(erangeflag); 220 testOneInput(func, x, IntegerMin, true); 221 } 222 } 223 224 void testSubnormalRange(RoundToIntegerFunc func) { 225 constexpr UIntType count = 1000001; 226 constexpr UIntType step = 227 (FPBits::maxSubnormal - FPBits::minSubnormal) / count; 228 for (UIntType i = FPBits::minSubnormal; i <= FPBits::maxSubnormal; 229 i += step) { 230 F x = F(FPBits(i)); 231 if (x == F(0.0)) 232 continue; 233 // All subnormal numbers should round to zero. 234 if (TestModes) { 235 if (x > 0) { 236 __llvm_libc::fputil::setRound(FE_UPWARD); 237 testOneInput(func, x, I(1), false); 238 __llvm_libc::fputil::setRound(FE_DOWNWARD); 239 testOneInput(func, x, I(0), false); 240 __llvm_libc::fputil::setRound(FE_TOWARDZERO); 241 testOneInput(func, x, I(0), false); 242 __llvm_libc::fputil::setRound(FE_TONEAREST); 243 testOneInput(func, x, I(0), false); 244 } else { 245 __llvm_libc::fputil::setRound(FE_UPWARD); 246 testOneInput(func, x, I(0), false); 247 __llvm_libc::fputil::setRound(FE_DOWNWARD); 248 testOneInput(func, x, I(-1), false); 249 __llvm_libc::fputil::setRound(FE_TOWARDZERO); 250 testOneInput(func, x, I(0), false); 251 __llvm_libc::fputil::setRound(FE_TONEAREST); 252 testOneInput(func, x, I(0), false); 253 } 254 } else { 255 testOneInput(func, x, 0L, false); 256 } 257 } 258 } 259 260 void testNormalRange(RoundToIntegerFunc func) { 261 // This function compares with an equivalent MPFR function which rounds 262 // floating point numbers to long values. There is no MPFR function to 263 // round to long long or wider integer values. So, we will peform the 264 // comparisons in this function only if the width of I less than equal to 265 // that of long. 266 if (sizeof(I) > sizeof(long)) 267 return; 268 269 constexpr UIntType count = 1000001; 270 constexpr UIntType step = (FPBits::maxNormal - FPBits::minNormal) / count; 271 for (UIntType i = FPBits::minNormal; i <= FPBits::maxNormal; i += step) { 272 F x = F(FPBits(i)); 273 // In normal range on x86 platforms, the long double implicit 1 bit can be 274 // zero making the numbers NaN. We will skip them. 275 if (isnan(x)) { 276 continue; 277 } 278 279 if (TestModes) { 280 for (int m : roundingModes) { 281 long mpfrLongResult; 282 bool erangeflag = 283 mpfr::RoundToLong(x, toMPFRRoundingMode(m), mpfrLongResult); 284 I mpfrResult = mpfrLongResult; 285 __llvm_libc::fputil::setRound(m); 286 if (erangeflag) 287 testOneInput(func, x, x > 0 ? IntegerMax : IntegerMin, true); 288 else 289 testOneInput(func, x, mpfrResult, false); 290 } 291 } else { 292 long mpfrLongResult; 293 bool erangeflag = mpfr::RoundToLong(x, mpfrLongResult); 294 I mpfrResult = mpfrLongResult; 295 if (erangeflag) 296 testOneInput(func, x, x > 0 ? IntegerMax : IntegerMin, true); 297 else 298 testOneInput(func, x, mpfrResult, false); 299 } 300 } 301 } 302 }; 303 304 #define LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, TestModes) \ 305 using LlvmLibcRoundToIntegerTest = \ 306 RoundToIntegerTestTemplate<F, I, TestModes>; \ 307 TEST_F(LlvmLibcRoundToIntegerTest, InfinityAndNaN) { \ 308 testInfinityAndNaN(&func); \ 309 } \ 310 TEST_F(LlvmLibcRoundToIntegerTest, RoundNumbers) { \ 311 testRoundNumbers(&func); \ 312 } \ 313 TEST_F(LlvmLibcRoundToIntegerTest, Fractions) { testFractions(&func); } \ 314 TEST_F(LlvmLibcRoundToIntegerTest, IntegerOverflow) { \ 315 testIntegerOverflow(&func); \ 316 } \ 317 TEST_F(LlvmLibcRoundToIntegerTest, SubnormalRange) { \ 318 testSubnormalRange(&func); \ 319 } \ 320 TEST_F(LlvmLibcRoundToIntegerTest, NormalRange) { testNormalRange(&func); } 321 322 #define LIST_ROUND_TO_INTEGER_TESTS(F, I, func) \ 323 LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, false) 324 325 #define LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(F, I, func) \ 326 LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, true) 327 328 #endif // LLVM_LIBC_TEST_SRC_MATH_ROUNDTOINTEGERTEST_H 329