1 //===-- MPFRUtils.h ---------------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H
10 #define LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H
12 #include "src/__support/CPP/type_traits.h"
13 #include "test/UnitTest/RoundingModeUtils.h"
14 #include "test/UnitTest/Test.h"
18 namespace LIBC_NAMESPACE
{
22 enum class Operation
: int {
23 // Operations with take a single floating point number as input
24 // and produce a single floating point number as output. The input
25 // and output floating point numbers are of the same kind.
26 BeginUnaryOperationsSingleOutput
,
57 EndUnaryOperationsSingleOutput
,
59 // Operations which take a single floating point nubmer as input
60 // but produce two outputs. The first ouput is a floating point
61 // number of the same type as the input. The second output is of type
63 BeginUnaryOperationsTwoOutputs
,
64 Frexp
, // Floating point output, the first output, is the fractional part.
65 EndUnaryOperationsTwoOutputs
,
67 // Operations wich take two floating point nubmers of the same type as
68 // input and produce a single floating point number of the same type as
70 BeginBinaryOperationsSingleOutput
,
73 EndBinaryOperationsSingleOutput
,
75 // Operations which take two floating point numbers of the same type as
76 // input and produce two outputs. The first output is a floating nubmer of
77 // the same type as the inputs. The second output is af type 'int'.
78 BeginBinaryOperationsTwoOutputs
,
79 RemQuo
, // The first output, the floating point output, is the remainder.
80 EndBinaryOperationsTwoOutputs
,
82 // Operations which take three floating point nubmers of the same type as
83 // input and produce a single floating point number of the same type as
85 BeginTernaryOperationsSingleOuput
,
87 EndTernaryOperationsSingleOutput
,
90 using LIBC_NAMESPACE::fputil::testing::ForceRoundingMode
;
91 using LIBC_NAMESPACE::fputil::testing::RoundingMode
;
93 template <typename T
> struct BinaryInput
{
95 LIBC_NAMESPACE::cpp::is_floating_point_v
<T
>,
96 "Template parameter of BinaryInput must be a floating point type.");
102 template <typename T
> struct TernaryInput
{
104 LIBC_NAMESPACE::cpp::is_floating_point_v
<T
>,
105 "Template parameter of TernaryInput must be a floating point type.");
111 template <typename T
> struct BinaryOutput
{
118 template <typename T1
, typename T2
>
119 struct AreMatchingBinaryInputAndBinaryOutput
{
120 static constexpr bool VALUE
= false;
123 template <typename T
>
124 struct AreMatchingBinaryInputAndBinaryOutput
<BinaryInput
<T
>, BinaryOutput
<T
>> {
125 static constexpr bool VALUE
= cpp::is_floating_point_v
<T
>;
128 template <typename T
>
129 bool compare_unary_operation_single_output(Operation op
, T input
, T libc_output
,
130 double ulp_tolerance
,
131 RoundingMode rounding
);
132 template <typename T
>
133 bool compare_unary_operation_two_outputs(Operation op
, T input
,
134 const BinaryOutput
<T
> &libc_output
,
135 double ulp_tolerance
,
136 RoundingMode rounding
);
137 template <typename T
>
138 bool compare_binary_operation_two_outputs(Operation op
,
139 const BinaryInput
<T
> &input
,
140 const BinaryOutput
<T
> &libc_output
,
141 double ulp_tolerance
,
142 RoundingMode rounding
);
144 template <typename T
>
145 bool compare_binary_operation_one_output(Operation op
,
146 const BinaryInput
<T
> &input
,
147 T libc_output
, double ulp_tolerance
,
148 RoundingMode rounding
);
150 template <typename T
>
151 bool compare_ternary_operation_one_output(Operation op
,
152 const TernaryInput
<T
> &input
,
153 T libc_output
, double ulp_tolerance
,
154 RoundingMode rounding
);
156 template <typename T
>
157 void explain_unary_operation_single_output_error(Operation op
, T input
,
159 double ulp_tolerance
,
160 RoundingMode rounding
);
161 template <typename T
>
162 void explain_unary_operation_two_outputs_error(
163 Operation op
, T input
, const BinaryOutput
<T
> &match_value
,
164 double ulp_tolerance
, RoundingMode rounding
);
165 template <typename T
>
166 void explain_binary_operation_two_outputs_error(
167 Operation op
, const BinaryInput
<T
> &input
,
168 const BinaryOutput
<T
> &match_value
, double ulp_tolerance
,
169 RoundingMode rounding
);
171 template <typename T
>
172 void explain_binary_operation_one_output_error(Operation op
,
173 const BinaryInput
<T
> &input
,
175 double ulp_tolerance
,
176 RoundingMode rounding
);
178 template <typename T
>
179 void explain_ternary_operation_one_output_error(Operation op
,
180 const TernaryInput
<T
> &input
,
182 double ulp_tolerance
,
183 RoundingMode rounding
);
185 template <Operation op
, bool silent
, typename InputType
, typename OutputType
>
186 class MPFRMatcher
: public testing::Matcher
<OutputType
> {
188 OutputType match_value
;
189 double ulp_tolerance
;
190 RoundingMode rounding
;
193 MPFRMatcher(InputType testInput
, double ulp_tolerance
, RoundingMode rounding
)
194 : input(testInput
), ulp_tolerance(ulp_tolerance
), rounding(rounding
) {}
196 bool match(OutputType libcResult
) {
197 match_value
= libcResult
;
198 return match(input
, match_value
);
201 // This method is marked with NOLINT because the name `explainError` does not
202 // conform to the coding style.
203 void explainError() override
{ // NOLINT
204 explain_error(input
, match_value
);
207 // Whether the `explainError` step is skipped or not.
208 bool is_silent() const override
{ return silent
; }
211 template <typename T
> bool match(T in
, T out
) {
212 return compare_unary_operation_single_output(op
, in
, out
, ulp_tolerance
,
216 template <typename T
> bool match(T in
, const BinaryOutput
<T
> &out
) {
217 return compare_unary_operation_two_outputs(op
, in
, out
, ulp_tolerance
,
221 template <typename T
> bool match(const BinaryInput
<T
> &in
, T out
) {
222 return compare_binary_operation_one_output(op
, in
, out
, ulp_tolerance
,
226 template <typename T
>
227 bool match(BinaryInput
<T
> in
, const BinaryOutput
<T
> &out
) {
228 return compare_binary_operation_two_outputs(op
, in
, out
, ulp_tolerance
,
232 template <typename T
> bool match(const TernaryInput
<T
> &in
, T out
) {
233 return compare_ternary_operation_one_output(op
, in
, out
, ulp_tolerance
,
237 template <typename T
> void explain_error(T in
, T out
) {
238 explain_unary_operation_single_output_error(op
, in
, out
, ulp_tolerance
,
242 template <typename T
> void explain_error(T in
, const BinaryOutput
<T
> &out
) {
243 explain_unary_operation_two_outputs_error(op
, in
, out
, ulp_tolerance
,
247 template <typename T
>
248 void explain_error(const BinaryInput
<T
> &in
, const BinaryOutput
<T
> &out
) {
249 explain_binary_operation_two_outputs_error(op
, in
, out
, ulp_tolerance
,
253 template <typename T
> void explain_error(const BinaryInput
<T
> &in
, T out
) {
254 explain_binary_operation_one_output_error(op
, in
, out
, ulp_tolerance
,
258 template <typename T
> void explain_error(const TernaryInput
<T
> &in
, T out
) {
259 explain_ternary_operation_one_output_error(op
, in
, out
, ulp_tolerance
,
264 } // namespace internal
266 // Return true if the input and ouput types for the operation op are valid
268 template <Operation op
, typename InputType
, typename OutputType
>
269 constexpr bool is_valid_operation() {
270 return (Operation::BeginUnaryOperationsSingleOutput
< op
&&
271 op
< Operation::EndUnaryOperationsSingleOutput
&&
272 cpp::is_same_v
<InputType
, OutputType
> &&
273 cpp::is_floating_point_v
<InputType
>) ||
274 (Operation::BeginUnaryOperationsTwoOutputs
< op
&&
275 op
< Operation::EndUnaryOperationsTwoOutputs
&&
276 cpp::is_floating_point_v
<InputType
> &&
277 cpp::is_same_v
<OutputType
, BinaryOutput
<InputType
>>) ||
278 (Operation::BeginBinaryOperationsSingleOutput
< op
&&
279 op
< Operation::EndBinaryOperationsSingleOutput
&&
280 cpp::is_floating_point_v
<OutputType
> &&
281 cpp::is_same_v
<InputType
, BinaryInput
<OutputType
>>) ||
282 (Operation::BeginBinaryOperationsTwoOutputs
< op
&&
283 op
< Operation::EndBinaryOperationsTwoOutputs
&&
284 internal::AreMatchingBinaryInputAndBinaryOutput
<InputType
,
285 OutputType
>::VALUE
) ||
286 (Operation::BeginTernaryOperationsSingleOuput
< op
&&
287 op
< Operation::EndTernaryOperationsSingleOutput
&&
288 cpp::is_floating_point_v
<OutputType
> &&
289 cpp::is_same_v
<InputType
, TernaryInput
<OutputType
>>);
292 template <Operation op
, typename InputType
, typename OutputType
>
293 __attribute__((no_sanitize("address"))) cpp::enable_if_t
<
294 is_valid_operation
<op
, InputType
, OutputType
>(),
295 internal::MPFRMatcher
<op
, /*is_silent*/ false, InputType
, OutputType
>>
296 get_mpfr_matcher(InputType input
, OutputType output_unused
,
297 double ulp_tolerance
, RoundingMode rounding
) {
298 return internal::MPFRMatcher
<op
, /*is_silent*/ false, InputType
, OutputType
>(
299 input
, ulp_tolerance
, rounding
);
302 template <Operation op
, typename InputType
, typename OutputType
>
303 __attribute__((no_sanitize("address"))) cpp::enable_if_t
<
304 is_valid_operation
<op
, InputType
, OutputType
>(),
305 internal::MPFRMatcher
<op
, /*is_silent*/ true, InputType
, OutputType
>>
306 get_silent_mpfr_matcher(InputType input
, OutputType output_unused
,
307 double ulp_tolerance
, RoundingMode rounding
) {
308 return internal::MPFRMatcher
<op
, /*is_silent*/ true, InputType
, OutputType
>(
309 input
, ulp_tolerance
, rounding
);
312 template <typename T
> T
round(T x
, RoundingMode mode
);
314 template <typename T
> bool round_to_long(T x
, long &result
);
315 template <typename T
> bool round_to_long(T x
, RoundingMode mode
, long &result
);
318 } // namespace testing
319 } // namespace LIBC_NAMESPACE
321 // GET_MPFR_DUMMY_ARG is going to be added to the end of GET_MPFR_MACRO as a
322 // simple way to avoid the compiler warning `gnu-zero-variadic-macro-arguments`.
323 #define GET_MPFR_DUMMY_ARG(...) 0
325 #define GET_MPFR_MACRO(__1, __2, __3, __4, __5, __NAME, ...) __NAME
327 #define EXPECT_MPFR_MATCH_DEFAULT(op, input, match_value, ulp_tolerance) \
328 EXPECT_THAT(match_value, \
329 LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>( \
330 input, match_value, ulp_tolerance, \
331 LIBC_NAMESPACE::testing::mpfr::RoundingMode::Nearest))
333 #define EXPECT_MPFR_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \
335 EXPECT_THAT(match_value, \
336 LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>( \
337 input, match_value, ulp_tolerance, rounding))
339 #define EXPECT_MPFR_MATCH(...) \
340 GET_MPFR_MACRO(__VA_ARGS__, EXPECT_MPFR_MATCH_ROUNDING, \
341 EXPECT_MPFR_MATCH_DEFAULT, GET_MPFR_DUMMY_ARG) \
344 #define TEST_MPFR_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \
346 LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>(input, match_value, \
347 ulp_tolerance, rounding) \
350 #define TEST_MPFR_MATCH(...) \
351 GET_MPFR_MACRO(__VA_ARGS__, TEST_MPFR_MATCH_ROUNDING, \
352 EXPECT_MPFR_MATCH_DEFAULT, GET_MPFR_DUMMY_ARG) \
355 #define EXPECT_MPFR_MATCH_ALL_ROUNDING(op, input, match_value, ulp_tolerance) \
357 namespace mpfr = LIBC_NAMESPACE::testing::mpfr; \
358 mpfr::ForceRoundingMode __r1(mpfr::RoundingMode::Nearest); \
359 if (__r1.success) { \
360 EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
361 mpfr::RoundingMode::Nearest); \
363 mpfr::ForceRoundingMode __r2(mpfr::RoundingMode::Upward); \
364 if (__r2.success) { \
365 EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
366 mpfr::RoundingMode::Upward); \
368 mpfr::ForceRoundingMode __r3(mpfr::RoundingMode::Downward); \
369 if (__r3.success) { \
370 EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
371 mpfr::RoundingMode::Downward); \
373 mpfr::ForceRoundingMode __r4(mpfr::RoundingMode::TowardZero); \
374 if (__r4.success) { \
375 EXPECT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
376 mpfr::RoundingMode::TowardZero); \
380 #define TEST_MPFR_MATCH_ROUNDING_SILENTLY(op, input, match_value, \
381 ulp_tolerance, rounding) \
382 LIBC_NAMESPACE::testing::mpfr::get_silent_mpfr_matcher<op>( \
383 input, match_value, ulp_tolerance, rounding) \
386 #define ASSERT_MPFR_MATCH_DEFAULT(op, input, match_value, ulp_tolerance) \
387 ASSERT_THAT(match_value, \
388 LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>( \
389 input, match_value, ulp_tolerance, \
390 LIBC_NAMESPACE::testing::mpfr::RoundingMode::Nearest))
392 #define ASSERT_MPFR_MATCH_ROUNDING(op, input, match_value, ulp_tolerance, \
394 ASSERT_THAT(match_value, \
395 LIBC_NAMESPACE::testing::mpfr::get_mpfr_matcher<op>( \
396 input, match_value, ulp_tolerance, rounding))
398 #define ASSERT_MPFR_MATCH(...) \
399 GET_MPFR_MACRO(__VA_ARGS__, ASSERT_MPFR_MATCH_ROUNDING, \
400 ASSERT_MPFR_MATCH_DEFAULT, GET_MPFR_DUMMY_ARG) \
403 #define ASSERT_MPFR_MATCH_ALL_ROUNDING(op, input, match_value, ulp_tolerance) \
405 namespace mpfr = LIBC_NAMESPACE::testing::mpfr; \
406 mpfr::ForceRoundingMode __r1(mpfr::RoundingMode::Nearest); \
407 if (__r1.success) { \
408 ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
409 mpfr::RoundingMode::Nearest); \
411 mpfr::ForceRoundingMode __r2(mpfr::RoundingMode::Upward); \
412 if (__r2.success) { \
413 ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
414 mpfr::RoundingMode::Upward); \
416 mpfr::ForceRoundingMode __r3(mpfr::RoundingMode::Downward); \
417 if (__r3.success) { \
418 ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
419 mpfr::RoundingMode::Downward); \
421 mpfr::ForceRoundingMode __r4(mpfr::RoundingMode::TowardZero); \
422 if (__r4.success) { \
423 ASSERT_MPFR_MATCH(op, input, match_value, ulp_tolerance, \
424 mpfr::RoundingMode::TowardZero); \
428 #endif // LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H