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_SMOKE_ROUNDTOINTEGERTEST_H
10#define LLVM_LIBC_TEST_SRC_MATH_SMOKE_ROUNDTOINTEGERTEST_H
11
12#include "src/__support/CPP/algorithm.h"
13#include "src/__support/FPUtil/FEnvImpl.h"
14#include "src/__support/FPUtil/FPBits.h"
15#include "test/UnitTest/FEnvSafeTest.h"
16#include "test/UnitTest/FPMatcher.h"
17#include "test/UnitTest/Test.h"
18
19#include "hdr/math_macros.h"
20
21static constexpr int ROUNDING_MODES[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO,
22 FE_TONEAREST};
23
24template <typename F, typename I, bool TestModes = false>
25class RoundToIntegerTestTemplate
26 : public LIBC_NAMESPACE::testing::FEnvSafeTest {
27public:
28 typedef I (*RoundToIntegerFunc)(F);
29
30private:
31 DECLARE_SPECIAL_CONSTANTS(F)
32
33 static constexpr StorageType MAX_SUBNORMAL =
34 FPBits::max_subnormal().uintval();
35 static constexpr StorageType MIN_SUBNORMAL =
36 FPBits::min_subnormal().uintval();
37
38 static constexpr I INTEGER_MIN = I(1) << (sizeof(I) * 8 - 1);
39 static constexpr I INTEGER_MAX = -(INTEGER_MIN + 1);
40
41 void test_one_input(RoundToIntegerFunc func, F input, I expected,
42 bool expectError) {
43 libc_errno = 0;
44 LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
45
46 ASSERT_EQ(func(input), expected);
47
48 // TODO: Handle the !expectError case. It used to expect
49 // 0 for errno and exceptions, but this doesn't hold for
50 // all math functions using RoundToInteger test:
51 // https://github.com/llvm/llvm-project/pull/88816
52 if (expectError) {
53 ASSERT_FP_EXCEPTION(FE_INVALID);
54 ASSERT_MATH_ERRNO(EDOM);
55 }
56 }
57
58public:
59 void SetUp() override {
60 LIBC_NAMESPACE::testing::FEnvSafeTest::SetUp();
61
62 if (math_errhandling & MATH_ERREXCEPT) {
63 // We will disable all exceptions so that the test will not
64 // crash with SIGFPE. We can still use fetestexcept to check
65 // if the appropriate flag was raised.
66 LIBC_NAMESPACE::fputil::disable_except(FE_ALL_EXCEPT);
67 }
68 }
69
70 void do_infinity_and_na_n_test(RoundToIntegerFunc func) {
71 test_one_input(func, inf, INTEGER_MAX, true);
72 test_one_input(func, neg_inf, INTEGER_MIN, true);
73 // This is currently never enabled, the
74 // LLVM_LIBC_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR CMake option in
75 // libc/CMakeLists.txt is not forwarded to C++.
76#if LIBC_COPT_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR
77 // Result is not well-defined, we always returns INTEGER_MAX
78 test_one_input(func, aNaN, INTEGER_MAX, true);
79#endif // LIBC_COPT_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR
80 }
81
82 void testInfinityAndNaN(RoundToIntegerFunc func) {
83 if (TestModes) {
84 for (int mode : ROUNDING_MODES) {
85 LIBC_NAMESPACE::fputil::set_round(mode);
86 do_infinity_and_na_n_test(func);
87 }
88 } else {
89 do_infinity_and_na_n_test(func);
90 }
91 }
92
93 void do_round_numbers_test(RoundToIntegerFunc func) {
94 test_one_input(func, zero, I(0), false);
95 test_one_input(func, neg_zero, I(0), false);
96 test_one_input(func, F(1.0), I(1), false);
97 test_one_input(func, F(-1.0), I(-1), false);
98 test_one_input(func, F(10.0), I(10), false);
99 test_one_input(func, F(-10.0), I(-10), false);
100 test_one_input(func, F(1234.0), I(1234), false);
101 test_one_input(func, F(-1234.0), I(-1234), false);
102 }
103
104 void testRoundNumbers(RoundToIntegerFunc func) {
105 if (TestModes) {
106 for (int mode : ROUNDING_MODES) {
107 LIBC_NAMESPACE::fputil::set_round(mode);
108 do_round_numbers_test(func);
109 }
110 } else {
111 do_round_numbers_test(func);
112 }
113 }
114
115 void testSubnormalRange(RoundToIntegerFunc func) {
116 constexpr int COUNT = 1'000'001;
117 constexpr StorageType STEP = LIBC_NAMESPACE::cpp::max(
118 static_cast<StorageType>((MAX_SUBNORMAL - MIN_SUBNORMAL) / COUNT),
119 StorageType(1));
120 for (StorageType i = MIN_SUBNORMAL; i <= MAX_SUBNORMAL; i += STEP) {
121 F x = FPBits(i).get_val();
122 if (x == F(0.0))
123 continue;
124 // All subnormal numbers should round to zero.
125 if (TestModes) {
126 if (x > 0) {
127 LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);
128 test_one_input(func, x, I(1), false);
129 LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);
130 test_one_input(func, x, I(0), false);
131 LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);
132 test_one_input(func, x, I(0), false);
133 LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);
134 test_one_input(func, x, I(0), false);
135 } else {
136 LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);
137 test_one_input(func, x, I(0), false);
138 LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);
139 test_one_input(func, x, I(-1), false);
140 LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);
141 test_one_input(func, x, I(0), false);
142 LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);
143 test_one_input(func, x, I(0), false);
144 }
145 } else {
146 test_one_input(func, x, 0L, false);
147 }
148 }
149 }
150};
151
152#define LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, TestModes) \
153 using LlvmLibcRoundToIntegerTest = \
154 RoundToIntegerTestTemplate<F, I, TestModes>; \
155 TEST_F(LlvmLibcRoundToIntegerTest, InfinityAndNaN) { \
156 testInfinityAndNaN(&func); \
157 } \
158 TEST_F(LlvmLibcRoundToIntegerTest, RoundNumbers) { \
159 testRoundNumbers(&func); \
160 } \
161 TEST_F(LlvmLibcRoundToIntegerTest, SubnormalRange) { \
162 testSubnormalRange(&func); \
163 }
164
165#define LIST_ROUND_TO_INTEGER_TESTS(F, I, func) \
166 LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, false)
167
168// The GPU target does not support different rounding modes.
169#ifdef LIBC_TARGET_ARCH_IS_GPU
170#define LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(F, I, func) \
171 LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, false)
172#else
173#define LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(F, I, func) \
174 LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, true)
175#endif
176
177#endif // LLVM_LIBC_TEST_SRC_MATH_SMOKE_ROUNDTOINTEGERTEST_H
178

source code of libc/test/src/math/smoke/RoundToIntegerTest.h