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