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

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