1 //===-- Utils which wrap MPFR ---------------------------------------------===//
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 //===----------------------------------------------------------------------===//
11 #include "src/__support/CPP/string.h"
12 #include "src/__support/CPP/string_view.h"
13 #include "src/__support/FPUtil/FPBits.h"
14 #include "src/__support/FPUtil/FloatProperties.h"
15 #include "src/__support/FPUtil/PlatformDefs.h"
16 #include "src/__support/FPUtil/fpbits_str.h"
17 #include "test/UnitTest/FPMatcher.h"
26 template <typename T
> using FPBits
= LIBC_NAMESPACE::fputil::FPBits
<T
>;
28 namespace LIBC_NAMESPACE
{
32 // A precision value which allows sufficiently large additional
33 // precision compared to the floating point precision.
34 template <typename T
> struct ExtraPrecision
;
36 template <> struct ExtraPrecision
<float> {
37 static constexpr unsigned int VALUE
= 128;
40 template <> struct ExtraPrecision
<double> {
41 static constexpr unsigned int VALUE
= 256;
44 template <> struct ExtraPrecision
<long double> {
45 static constexpr unsigned int VALUE
= 256;
48 // If the ulp tolerance is less than or equal to 0.5, we would check that the
49 // result is rounded correctly with respect to the rounding mode by using the
50 // same precision as the inputs.
52 static inline unsigned int get_precision(double ulp_tolerance
) {
53 if (ulp_tolerance
<= 0.5) {
54 return LIBC_NAMESPACE::fputil::FloatProperties
<T
>::MANTISSA_PRECISION
;
56 return ExtraPrecision
<T
>::VALUE
;
60 static inline mpfr_rnd_t
get_mpfr_rounding_mode(RoundingMode mode
) {
62 case RoundingMode::Upward
:
65 case RoundingMode::Downward
:
68 case RoundingMode::TowardZero
:
71 case RoundingMode::Nearest
:
75 __builtin_unreachable();
79 unsigned int mpfr_precision
;
80 mpfr_rnd_t mpfr_rounding
;
85 MPFRNumber() : mpfr_precision(256), mpfr_rounding(MPFR_RNDN
) {
86 mpfr_init2(value
, mpfr_precision
);
89 // We use explicit EnableIf specializations to disallow implicit
90 // conversions. Implicit conversions can potentially lead to loss of
92 template <typename XType
,
93 cpp::enable_if_t
<cpp::is_same_v
<float, XType
>, int> = 0>
94 explicit MPFRNumber(XType x
, int precision
= ExtraPrecision
<XType
>::VALUE
,
95 RoundingMode rounding
= RoundingMode::Nearest
)
96 : mpfr_precision(precision
),
97 mpfr_rounding(get_mpfr_rounding_mode(rounding
)) {
98 mpfr_init2(value
, mpfr_precision
);
99 mpfr_set_flt(value
, x
, mpfr_rounding
);
102 template <typename XType
,
103 cpp::enable_if_t
<cpp::is_same_v
<double, XType
>, int> = 0>
104 explicit MPFRNumber(XType x
, int precision
= ExtraPrecision
<XType
>::VALUE
,
105 RoundingMode rounding
= RoundingMode::Nearest
)
106 : mpfr_precision(precision
),
107 mpfr_rounding(get_mpfr_rounding_mode(rounding
)) {
108 mpfr_init2(value
, mpfr_precision
);
109 mpfr_set_d(value
, x
, mpfr_rounding
);
112 template <typename XType
,
113 cpp::enable_if_t
<cpp::is_same_v
<long double, XType
>, int> = 0>
114 explicit MPFRNumber(XType x
, int precision
= ExtraPrecision
<XType
>::VALUE
,
115 RoundingMode rounding
= RoundingMode::Nearest
)
116 : mpfr_precision(precision
),
117 mpfr_rounding(get_mpfr_rounding_mode(rounding
)) {
118 mpfr_init2(value
, mpfr_precision
);
119 mpfr_set_ld(value
, x
, mpfr_rounding
);
122 template <typename XType
,
123 cpp::enable_if_t
<cpp::is_integral_v
<XType
>, int> = 0>
124 explicit MPFRNumber(XType x
, int precision
= ExtraPrecision
<float>::VALUE
,
125 RoundingMode rounding
= RoundingMode::Nearest
)
126 : mpfr_precision(precision
),
127 mpfr_rounding(get_mpfr_rounding_mode(rounding
)) {
128 mpfr_init2(value
, mpfr_precision
);
129 mpfr_set_sj(value
, x
, mpfr_rounding
);
132 MPFRNumber(const MPFRNumber
&other
)
133 : mpfr_precision(other
.mpfr_precision
),
134 mpfr_rounding(other
.mpfr_rounding
) {
135 mpfr_init2(value
, mpfr_precision
);
136 mpfr_set(value
, other
.value
, mpfr_rounding
);
139 ~MPFRNumber() { mpfr_clear(value
); }
141 MPFRNumber
&operator=(const MPFRNumber
&rhs
) {
142 mpfr_precision
= rhs
.mpfr_precision
;
143 mpfr_rounding
= rhs
.mpfr_rounding
;
144 mpfr_set(value
, rhs
.value
, mpfr_rounding
);
148 bool is_nan() const { return mpfr_nan_p(value
); }
150 MPFRNumber
abs() const {
151 MPFRNumber
result(*this);
152 mpfr_abs(result
.value
, value
, mpfr_rounding
);
156 MPFRNumber
acos() const {
157 MPFRNumber
result(*this);
158 mpfr_acos(result
.value
, value
, mpfr_rounding
);
162 MPFRNumber
acosh() const {
163 MPFRNumber
result(*this);
164 mpfr_acosh(result
.value
, value
, mpfr_rounding
);
168 MPFRNumber
asin() const {
169 MPFRNumber
result(*this);
170 mpfr_asin(result
.value
, value
, mpfr_rounding
);
174 MPFRNumber
asinh() const {
175 MPFRNumber
result(*this);
176 mpfr_asinh(result
.value
, value
, mpfr_rounding
);
180 MPFRNumber
atan() const {
181 MPFRNumber
result(*this);
182 mpfr_atan(result
.value
, value
, mpfr_rounding
);
186 MPFRNumber
atanh() const {
187 MPFRNumber
result(*this);
188 mpfr_atanh(result
.value
, value
, mpfr_rounding
);
192 MPFRNumber
ceil() const {
193 MPFRNumber
result(*this);
194 mpfr_ceil(result
.value
, value
);
198 MPFRNumber
cos() const {
199 MPFRNumber
result(*this);
200 mpfr_cos(result
.value
, value
, mpfr_rounding
);
204 MPFRNumber
cosh() const {
205 MPFRNumber
result(*this);
206 mpfr_cosh(result
.value
, value
, mpfr_rounding
);
210 MPFRNumber
erf() const {
211 MPFRNumber
result(*this);
212 mpfr_erf(result
.value
, value
, mpfr_rounding
);
216 MPFRNumber
exp() const {
217 MPFRNumber
result(*this);
218 mpfr_exp(result
.value
, value
, mpfr_rounding
);
222 MPFRNumber
exp2() const {
223 MPFRNumber
result(*this);
224 mpfr_exp2(result
.value
, value
, mpfr_rounding
);
228 MPFRNumber
exp10() const {
229 MPFRNumber
result(*this);
230 mpfr_exp10(result
.value
, value
, mpfr_rounding
);
234 MPFRNumber
expm1() const {
235 MPFRNumber
result(*this);
236 mpfr_expm1(result
.value
, value
, mpfr_rounding
);
240 MPFRNumber
floor() const {
241 MPFRNumber
result(*this);
242 mpfr_floor(result
.value
, value
);
246 MPFRNumber
fmod(const MPFRNumber
&b
) {
247 MPFRNumber
result(*this);
248 mpfr_fmod(result
.value
, value
, b
.value
, mpfr_rounding
);
252 MPFRNumber
frexp(int &exp
) {
253 MPFRNumber
result(*this);
254 mpfr_exp_t resultExp
;
255 mpfr_frexp(&resultExp
, result
.value
, value
, mpfr_rounding
);
260 MPFRNumber
hypot(const MPFRNumber
&b
) {
261 MPFRNumber
result(*this);
262 mpfr_hypot(result
.value
, value
, b
.value
, mpfr_rounding
);
266 MPFRNumber
log() const {
267 MPFRNumber
result(*this);
268 mpfr_log(result
.value
, value
, mpfr_rounding
);
272 MPFRNumber
log2() const {
273 MPFRNumber
result(*this);
274 mpfr_log2(result
.value
, value
, mpfr_rounding
);
278 MPFRNumber
log10() const {
279 MPFRNumber
result(*this);
280 mpfr_log10(result
.value
, value
, mpfr_rounding
);
284 MPFRNumber
log1p() const {
285 MPFRNumber
result(*this);
286 mpfr_log1p(result
.value
, value
, mpfr_rounding
);
290 MPFRNumber
remquo(const MPFRNumber
&divisor
, int "ient
) {
291 MPFRNumber
remainder(*this);
293 mpfr_remquo(remainder
.value
, &q
, value
, divisor
.value
, mpfr_rounding
);
298 MPFRNumber
round() const {
299 MPFRNumber
result(*this);
300 mpfr_round(result
.value
, value
);
304 bool round_to_long(long &result
) const {
305 // We first calculate the rounded value. This way, when converting
306 // to long using mpfr_get_si, the rounding direction of MPFR_RNDN
307 // (or any other rounding mode), does not have an influence.
308 MPFRNumber roundedValue
= round();
309 mpfr_clear_erangeflag();
310 result
= mpfr_get_si(roundedValue
.value
, MPFR_RNDN
);
311 return mpfr_erangeflag_p();
314 bool round_to_long(mpfr_rnd_t rnd
, long &result
) const {
315 MPFRNumber
rint_result(*this);
316 mpfr_rint(rint_result
.value
, value
, rnd
);
317 return rint_result
.round_to_long(result
);
320 MPFRNumber
rint(mpfr_rnd_t rnd
) const {
321 MPFRNumber
result(*this);
322 mpfr_rint(result
.value
, value
, rnd
);
326 MPFRNumber
mod_2pi() const {
327 MPFRNumber
result(0.0, 1280);
328 MPFRNumber
_2pi(0.0, 1280);
329 mpfr_const_pi(_2pi
.value
, MPFR_RNDN
);
330 mpfr_mul_si(_2pi
.value
, _2pi
.value
, 2, MPFR_RNDN
);
331 mpfr_fmod(result
.value
, value
, _2pi
.value
, MPFR_RNDN
);
335 MPFRNumber
mod_pi_over_2() const {
336 MPFRNumber
result(0.0, 1280);
337 MPFRNumber
pi_over_2(0.0, 1280);
338 mpfr_const_pi(pi_over_2
.value
, MPFR_RNDN
);
339 mpfr_mul_d(pi_over_2
.value
, pi_over_2
.value
, 0.5, MPFR_RNDN
);
340 mpfr_fmod(result
.value
, value
, pi_over_2
.value
, MPFR_RNDN
);
344 MPFRNumber
mod_pi_over_4() const {
345 MPFRNumber
result(0.0, 1280);
346 MPFRNumber
pi_over_4(0.0, 1280);
347 mpfr_const_pi(pi_over_4
.value
, MPFR_RNDN
);
348 mpfr_mul_d(pi_over_4
.value
, pi_over_4
.value
, 0.25, MPFR_RNDN
);
349 mpfr_fmod(result
.value
, value
, pi_over_4
.value
, MPFR_RNDN
);
353 MPFRNumber
sin() const {
354 MPFRNumber
result(*this);
355 mpfr_sin(result
.value
, value
, mpfr_rounding
);
359 MPFRNumber
sinh() const {
360 MPFRNumber
result(*this);
361 mpfr_sinh(result
.value
, value
, mpfr_rounding
);
365 MPFRNumber
sqrt() const {
366 MPFRNumber
result(*this);
367 mpfr_sqrt(result
.value
, value
, mpfr_rounding
);
371 MPFRNumber
tan() const {
372 MPFRNumber
result(*this);
373 mpfr_tan(result
.value
, value
, mpfr_rounding
);
377 MPFRNumber
tanh() const {
378 MPFRNumber
result(*this);
379 mpfr_tanh(result
.value
, value
, mpfr_rounding
);
383 MPFRNumber
trunc() const {
384 MPFRNumber
result(*this);
385 mpfr_trunc(result
.value
, value
);
389 MPFRNumber
fma(const MPFRNumber
&b
, const MPFRNumber
&c
) {
390 MPFRNumber
result(*this);
391 mpfr_fma(result
.value
, value
, b
.value
, c
.value
, mpfr_rounding
);
395 cpp::string
str() const {
396 // 200 bytes should be more than sufficient to hold a 100-digit number
397 // plus additional bytes for the decimal point, '-' sign etc.
398 constexpr size_t printBufSize
= 200;
399 char buffer
[printBufSize
];
400 mpfr_snprintf(buffer
, printBufSize
, "%100.50Rf", value
);
401 cpp::string_view
view(buffer
);
403 const char whitespace
= ' ';
404 while (!view
.empty() && view
.front() == whitespace
)
405 view
.remove_prefix(1);
406 while (!view
.empty() && view
.back() == whitespace
)
407 view
.remove_suffix(1);
408 return cpp::string(view
.data());
411 // These functions are useful for debugging.
412 template <typename T
> T
as() const;
414 void dump(const char *msg
) const { mpfr_printf("%s%.128Rf\n", msg
, value
); }
416 // Return the ULP (units-in-the-last-place) difference between the
417 // stored MPFR and a floating point number.
419 // We define ULP difference as follows:
420 // If exponents of this value and the |input| are same, then:
421 // ULP(this_value, input) = abs(this_value - input) / eps(input)
423 // max = max(abs(this_value), abs(input))
424 // min = min(abs(this_value), abs(input))
425 // maxExponent = exponent(max)
426 // ULP(this_value, input) = (max - 2^maxExponent) / eps(max) +
427 // (2^maxExponent - min) / eps(min)
430 // 1. A ULP of 0.0 will imply that the value is correctly rounded.
431 // 2. We expect that this value and the value to be compared (the [input]
432 // argument) are reasonable close, and we will provide an upper bound
433 // of ULP value for testing. Morever, most of the fractional parts of
434 // ULP value do not matter much, so using double as the return type
435 // should be good enough.
436 // 3. For close enough values (values which don't diff in their exponent by
437 // not more than 1), a ULP difference of N indicates a bit distance
438 // of N between this number and [input].
439 // 4. A values of +0.0 and -0.0 are treated as equal.
440 template <typename T
>
441 cpp::enable_if_t
<cpp::is_floating_point_v
<T
>, MPFRNumber
>
442 ulp_as_mpfr_number(T input
) {
444 if (thisAsT
== input
)
445 return MPFRNumber(0.0);
448 if (fputil::FPBits
<T
>(input
).is_nan())
449 return MPFRNumber(0.0);
450 return MPFRNumber(static_cast<T
>(fputil::FPBits
<T
>::inf()));
453 int thisExponent
= fputil::FPBits
<T
>(thisAsT
).get_exponent();
454 int inputExponent
= fputil::FPBits
<T
>(input
).get_exponent();
455 // Adjust the exponents for denormal numbers.
456 if (fputil::FPBits
<T
>(thisAsT
).get_unbiased_exponent() == 0)
458 if (fputil::FPBits
<T
>(input
).get_unbiased_exponent() == 0)
461 if (thisAsT
* input
< 0 || thisExponent
== inputExponent
) {
462 MPFRNumber
inputMPFR(input
);
463 mpfr_sub(inputMPFR
.value
, value
, inputMPFR
.value
, MPFR_RNDN
);
464 mpfr_abs(inputMPFR
.value
, inputMPFR
.value
, MPFR_RNDN
);
465 mpfr_mul_2si(inputMPFR
.value
, inputMPFR
.value
,
466 -thisExponent
+ int(fputil::MantissaWidth
<T
>::VALUE
),
471 // If the control reaches here, it means that this number and input are
472 // of the same sign but different exponent. In such a case, ULP error is
473 // calculated as sum of two parts.
474 thisAsT
= std::abs(thisAsT
);
475 input
= std::abs(input
);
476 T min
= thisAsT
> input
? input
: thisAsT
;
477 T max
= thisAsT
> input
? thisAsT
: input
;
478 int minExponent
= fputil::FPBits
<T
>(min
).get_exponent();
479 int maxExponent
= fputil::FPBits
<T
>(max
).get_exponent();
480 // Adjust the exponents for denormal numbers.
481 if (fputil::FPBits
<T
>(min
).get_unbiased_exponent() == 0)
483 if (fputil::FPBits
<T
>(max
).get_unbiased_exponent() == 0)
486 MPFRNumber
minMPFR(min
);
487 MPFRNumber
maxMPFR(max
);
489 MPFRNumber
pivot(uint32_t(1));
490 mpfr_mul_2si(pivot
.value
, pivot
.value
, maxExponent
, MPFR_RNDN
);
492 mpfr_sub(minMPFR
.value
, pivot
.value
, minMPFR
.value
, MPFR_RNDN
);
493 mpfr_mul_2si(minMPFR
.value
, minMPFR
.value
,
494 -minExponent
+ int(fputil::MantissaWidth
<T
>::VALUE
),
497 mpfr_sub(maxMPFR
.value
, maxMPFR
.value
, pivot
.value
, MPFR_RNDN
);
498 mpfr_mul_2si(maxMPFR
.value
, maxMPFR
.value
,
499 -maxExponent
+ int(fputil::MantissaWidth
<T
>::VALUE
),
502 mpfr_add(minMPFR
.value
, minMPFR
.value
, maxMPFR
.value
, MPFR_RNDN
);
506 template <typename T
>
507 cpp::enable_if_t
<cpp::is_floating_point_v
<T
>, cpp::string
>
508 ulp_as_string(T input
) {
509 MPFRNumber num
= ulp_as_mpfr_number(input
);
513 template <typename T
>
514 cpp::enable_if_t
<cpp::is_floating_point_v
<T
>, double> ulp(T input
) {
515 MPFRNumber num
= ulp_as_mpfr_number(input
);
516 return num
.as
<double>();
520 template <> float MPFRNumber::as
<float>() const {
521 return mpfr_get_flt(value
, mpfr_rounding
);
524 template <> double MPFRNumber::as
<double>() const {
525 return mpfr_get_d(value
, mpfr_rounding
);
528 template <> long double MPFRNumber::as
<long double>() const {
529 return mpfr_get_ld(value
, mpfr_rounding
);
534 template <typename InputType
>
535 cpp::enable_if_t
<cpp::is_floating_point_v
<InputType
>, MPFRNumber
>
536 unary_operation(Operation op
, InputType input
, unsigned int precision
,
537 RoundingMode rounding
) {
538 MPFRNumber
mpfrInput(input
, precision
, rounding
);
541 return mpfrInput
.abs();
542 case Operation::Acos
:
543 return mpfrInput
.acos();
544 case Operation::Acosh
:
545 return mpfrInput
.acosh();
546 case Operation::Asin
:
547 return mpfrInput
.asin();
548 case Operation::Asinh
:
549 return mpfrInput
.asinh();
550 case Operation::Atan
:
551 return mpfrInput
.atan();
552 case Operation::Atanh
:
553 return mpfrInput
.atanh();
554 case Operation::Ceil
:
555 return mpfrInput
.ceil();
557 return mpfrInput
.cos();
558 case Operation::Cosh
:
559 return mpfrInput
.cosh();
561 return mpfrInput
.erf();
563 return mpfrInput
.exp();
564 case Operation::Exp2
:
565 return mpfrInput
.exp2();
566 case Operation::Exp10
:
567 return mpfrInput
.exp10();
568 case Operation::Expm1
:
569 return mpfrInput
.expm1();
570 case Operation::Floor
:
571 return mpfrInput
.floor();
573 return mpfrInput
.log();
574 case Operation::Log2
:
575 return mpfrInput
.log2();
576 case Operation::Log10
:
577 return mpfrInput
.log10();
578 case Operation::Log1p
:
579 return mpfrInput
.log1p();
580 case Operation::Mod2PI
:
581 return mpfrInput
.mod_2pi();
582 case Operation::ModPIOver2
:
583 return mpfrInput
.mod_pi_over_2();
584 case Operation::ModPIOver4
:
585 return mpfrInput
.mod_pi_over_4();
586 case Operation::Round
:
587 return mpfrInput
.round();
589 return mpfrInput
.sin();
590 case Operation::Sinh
:
591 return mpfrInput
.sinh();
592 case Operation::Sqrt
:
593 return mpfrInput
.sqrt();
595 return mpfrInput
.tan();
596 case Operation::Tanh
:
597 return mpfrInput
.tanh();
598 case Operation::Trunc
:
599 return mpfrInput
.trunc();
601 __builtin_unreachable();
605 template <typename InputType
>
606 cpp::enable_if_t
<cpp::is_floating_point_v
<InputType
>, MPFRNumber
>
607 unary_operation_two_outputs(Operation op
, InputType input
, int &output
,
608 unsigned int precision
, RoundingMode rounding
) {
609 MPFRNumber
mpfrInput(input
, precision
, rounding
);
611 case Operation::Frexp
:
612 return mpfrInput
.frexp(output
);
614 __builtin_unreachable();
618 template <typename InputType
>
619 cpp::enable_if_t
<cpp::is_floating_point_v
<InputType
>, MPFRNumber
>
620 binary_operation_one_output(Operation op
, InputType x
, InputType y
,
621 unsigned int precision
, RoundingMode rounding
) {
622 MPFRNumber
inputX(x
, precision
, rounding
);
623 MPFRNumber
inputY(y
, precision
, rounding
);
625 case Operation::Fmod
:
626 return inputX
.fmod(inputY
);
627 case Operation::Hypot
:
628 return inputX
.hypot(inputY
);
630 __builtin_unreachable();
634 template <typename InputType
>
635 cpp::enable_if_t
<cpp::is_floating_point_v
<InputType
>, MPFRNumber
>
636 binary_operation_two_outputs(Operation op
, InputType x
, InputType y
,
637 int &output
, unsigned int precision
,
638 RoundingMode rounding
) {
639 MPFRNumber
inputX(x
, precision
, rounding
);
640 MPFRNumber
inputY(y
, precision
, rounding
);
642 case Operation::RemQuo
:
643 return inputX
.remquo(inputY
, output
);
645 __builtin_unreachable();
649 template <typename InputType
>
650 cpp::enable_if_t
<cpp::is_floating_point_v
<InputType
>, MPFRNumber
>
651 ternary_operation_one_output(Operation op
, InputType x
, InputType y
,
652 InputType z
, unsigned int precision
,
653 RoundingMode rounding
) {
654 // For FMA function, we just need to compare with the mpfr_fma with the same
655 // precision as InputType. Using higher precision as the intermediate results
656 // to compare might incorrectly fail due to double-rounding errors.
657 MPFRNumber
inputX(x
, precision
, rounding
);
658 MPFRNumber
inputY(y
, precision
, rounding
);
659 MPFRNumber
inputZ(z
, precision
, rounding
);
662 return inputX
.fma(inputY
, inputZ
);
664 __builtin_unreachable();
668 // Remark: For all the explain_*_error functions, we will use std::stringstream
669 // to build the complete error messages before sending it to the outstream `OS`
670 // once at the end. This will stop the error messages from interleaving when
671 // the tests are running concurrently.
672 template <typename T
>
673 void explain_unary_operation_single_output_error(Operation op
, T input
,
675 double ulp_tolerance
,
676 RoundingMode rounding
) {
677 unsigned int precision
= get_precision
<T
>(ulp_tolerance
);
678 MPFRNumber
mpfrInput(input
, precision
);
679 MPFRNumber mpfr_result
;
680 mpfr_result
= unary_operation(op
, input
, precision
, rounding
);
681 MPFRNumber
mpfrMatchValue(matchValue
);
682 tlog
<< "Match value not within tolerance value of MPFR result:\n"
683 << " Input decimal: " << mpfrInput
.str() << '\n';
684 tlog
<< " Input bits: " << str(FPBits
<T
>(input
)) << '\n';
685 tlog
<< '\n' << " Match decimal: " << mpfrMatchValue
.str() << '\n';
686 tlog
<< " Match bits: " << str(FPBits
<T
>(matchValue
)) << '\n';
687 tlog
<< '\n' << " MPFR result: " << mpfr_result
.str() << '\n';
688 tlog
<< " MPFR rounded: " << str(FPBits
<T
>(mpfr_result
.as
<T
>())) << '\n';
690 tlog
<< " ULP error: "
691 << mpfr_result
.ulp_as_mpfr_number(matchValue
).str() << '\n';
694 template void explain_unary_operation_single_output_error
<float>(Operation op
,
698 template void explain_unary_operation_single_output_error
<double>(
699 Operation op
, double, double, double, RoundingMode
);
700 template void explain_unary_operation_single_output_error
<long double>(
701 Operation op
, long double, long double, double, RoundingMode
);
703 template <typename T
>
704 void explain_unary_operation_two_outputs_error(
705 Operation op
, T input
, const BinaryOutput
<T
> &libc_result
,
706 double ulp_tolerance
, RoundingMode rounding
) {
707 unsigned int precision
= get_precision
<T
>(ulp_tolerance
);
708 MPFRNumber
mpfrInput(input
, precision
);
710 MPFRNumber mpfr_result
= unary_operation_two_outputs(op
, input
, mpfrIntResult
,
711 precision
, rounding
);
713 if (mpfrIntResult
!= libc_result
.i
) {
714 tlog
<< "MPFR integral result: " << mpfrIntResult
<< '\n'
715 << "Libc integral result: " << libc_result
.i
<< '\n';
717 tlog
<< "Integral result from libc matches integral result from MPFR.\n";
720 MPFRNumber
mpfrMatchValue(libc_result
.f
);
722 << "Libc floating point result is not within tolerance value of the MPFR "
725 tlog
<< " Input decimal: " << mpfrInput
.str() << "\n\n";
727 tlog
<< "Libc floating point value: " << mpfrMatchValue
.str() << '\n';
728 tlog
<< " Libc floating point bits: " << str(FPBits
<T
>(libc_result
.f
))
732 tlog
<< " MPFR result: " << mpfr_result
.str() << '\n';
733 tlog
<< " MPFR rounded: " << str(FPBits
<T
>(mpfr_result
.as
<T
>()))
737 << mpfr_result
.ulp_as_mpfr_number(libc_result
.f
).str() << '\n';
740 template void explain_unary_operation_two_outputs_error
<float>(
741 Operation
, float, const BinaryOutput
<float> &, double, RoundingMode
);
742 template void explain_unary_operation_two_outputs_error
<double>(
743 Operation
, double, const BinaryOutput
<double> &, double, RoundingMode
);
744 template void explain_unary_operation_two_outputs_error
<long double>(
745 Operation
, long double, const BinaryOutput
<long double> &, double,
748 template <typename T
>
749 void explain_binary_operation_two_outputs_error(
750 Operation op
, const BinaryInput
<T
> &input
,
751 const BinaryOutput
<T
> &libc_result
, double ulp_tolerance
,
752 RoundingMode rounding
) {
753 unsigned int precision
= get_precision
<T
>(ulp_tolerance
);
754 MPFRNumber
mpfrX(input
.x
, precision
);
755 MPFRNumber
mpfrY(input
.y
, precision
);
757 MPFRNumber mpfr_result
= binary_operation_two_outputs(
758 op
, input
.x
, input
.y
, mpfrIntResult
, precision
, rounding
);
759 MPFRNumber
mpfrMatchValue(libc_result
.f
);
761 tlog
<< "Input decimal: x: " << mpfrX
.str() << " y: " << mpfrY
.str() << '\n'
762 << "MPFR integral result: " << mpfrIntResult
<< '\n'
763 << "Libc integral result: " << libc_result
.i
<< '\n'
764 << "Libc floating point result: " << mpfrMatchValue
.str() << '\n'
765 << " MPFR result: " << mpfr_result
.str() << '\n';
766 tlog
<< "Libc floating point result bits: " << str(FPBits
<T
>(libc_result
.f
))
768 tlog
<< " MPFR rounded bits: "
769 << str(FPBits
<T
>(mpfr_result
.as
<T
>())) << '\n';
770 tlog
<< "ULP error: " << mpfr_result
.ulp_as_mpfr_number(libc_result
.f
).str()
774 template void explain_binary_operation_two_outputs_error
<float>(
775 Operation
, const BinaryInput
<float> &, const BinaryOutput
<float> &, double,
777 template void explain_binary_operation_two_outputs_error
<double>(
778 Operation
, const BinaryInput
<double> &, const BinaryOutput
<double> &,
779 double, RoundingMode
);
780 template void explain_binary_operation_two_outputs_error
<long double>(
781 Operation
, const BinaryInput
<long double> &,
782 const BinaryOutput
<long double> &, double, RoundingMode
);
784 template <typename T
>
785 void explain_binary_operation_one_output_error(Operation op
,
786 const BinaryInput
<T
> &input
,
788 double ulp_tolerance
,
789 RoundingMode rounding
) {
790 unsigned int precision
= get_precision
<T
>(ulp_tolerance
);
791 MPFRNumber
mpfrX(input
.x
, precision
);
792 MPFRNumber
mpfrY(input
.y
, precision
);
793 FPBits
<T
> xbits(input
.x
);
794 FPBits
<T
> ybits(input
.y
);
795 MPFRNumber mpfr_result
=
796 binary_operation_one_output(op
, input
.x
, input
.y
, precision
, rounding
);
797 MPFRNumber
mpfrMatchValue(libc_result
);
799 tlog
<< "Input decimal: x: " << mpfrX
.str() << " y: " << mpfrY
.str() << '\n';
800 tlog
<< "First input bits: " << str(FPBits
<T
>(input
.x
)) << '\n';
801 tlog
<< "Second input bits: " << str(FPBits
<T
>(input
.y
)) << '\n';
803 tlog
<< "Libc result: " << mpfrMatchValue
.str() << '\n'
804 << "MPFR result: " << mpfr_result
.str() << '\n';
805 tlog
<< "Libc floating point result bits: " << str(FPBits
<T
>(libc_result
))
807 tlog
<< " MPFR rounded bits: "
808 << str(FPBits
<T
>(mpfr_result
.as
<T
>())) << '\n';
809 tlog
<< "ULP error: " << mpfr_result
.ulp_as_mpfr_number(libc_result
).str()
813 template void explain_binary_operation_one_output_error
<float>(
814 Operation
, const BinaryInput
<float> &, float, double, RoundingMode
);
815 template void explain_binary_operation_one_output_error
<double>(
816 Operation
, const BinaryInput
<double> &, double, double, RoundingMode
);
817 template void explain_binary_operation_one_output_error
<long double>(
818 Operation
, const BinaryInput
<long double> &, long double, double,
821 template <typename T
>
822 void explain_ternary_operation_one_output_error(Operation op
,
823 const TernaryInput
<T
> &input
,
825 double ulp_tolerance
,
826 RoundingMode rounding
) {
827 unsigned int precision
= get_precision
<T
>(ulp_tolerance
);
828 MPFRNumber
mpfrX(input
.x
, precision
);
829 MPFRNumber
mpfrY(input
.y
, precision
);
830 MPFRNumber
mpfrZ(input
.z
, precision
);
831 FPBits
<T
> xbits(input
.x
);
832 FPBits
<T
> ybits(input
.y
);
833 FPBits
<T
> zbits(input
.z
);
834 MPFRNumber mpfr_result
= ternary_operation_one_output(
835 op
, input
.x
, input
.y
, input
.z
, precision
, rounding
);
836 MPFRNumber
mpfrMatchValue(libc_result
);
838 tlog
<< "Input decimal: x: " << mpfrX
.str() << " y: " << mpfrY
.str()
839 << " z: " << mpfrZ
.str() << '\n';
840 tlog
<< " First input bits: " << str(FPBits
<T
>(input
.x
)) << '\n';
841 tlog
<< "Second input bits: " << str(FPBits
<T
>(input
.y
)) << '\n';
842 tlog
<< " Third input bits: " << str(FPBits
<T
>(input
.z
)) << '\n';
844 tlog
<< "Libc result: " << mpfrMatchValue
.str() << '\n'
845 << "MPFR result: " << mpfr_result
.str() << '\n';
846 tlog
<< "Libc floating point result bits: " << str(FPBits
<T
>(libc_result
))
848 tlog
<< " MPFR rounded bits: "
849 << str(FPBits
<T
>(mpfr_result
.as
<T
>())) << '\n';
850 tlog
<< "ULP error: " << mpfr_result
.ulp_as_mpfr_number(libc_result
).str()
854 template void explain_ternary_operation_one_output_error
<float>(
855 Operation
, const TernaryInput
<float> &, float, double, RoundingMode
);
856 template void explain_ternary_operation_one_output_error
<double>(
857 Operation
, const TernaryInput
<double> &, double, double, RoundingMode
);
858 template void explain_ternary_operation_one_output_error
<long double>(
859 Operation
, const TernaryInput
<long double> &, long double, double,
862 template <typename T
>
863 bool compare_unary_operation_single_output(Operation op
, T input
, T libc_result
,
864 double ulp_tolerance
,
865 RoundingMode rounding
) {
866 unsigned int precision
= get_precision
<T
>(ulp_tolerance
);
867 MPFRNumber mpfr_result
;
868 mpfr_result
= unary_operation(op
, input
, precision
, rounding
);
869 double ulp
= mpfr_result
.ulp(libc_result
);
870 return (ulp
<= ulp_tolerance
);
873 template bool compare_unary_operation_single_output
<float>(Operation
, float,
876 template bool compare_unary_operation_single_output
<double>(Operation
, double,
879 template bool compare_unary_operation_single_output
<long double>(
880 Operation
, long double, long double, double, RoundingMode
);
882 template <typename T
>
883 bool compare_unary_operation_two_outputs(Operation op
, T input
,
884 const BinaryOutput
<T
> &libc_result
,
885 double ulp_tolerance
,
886 RoundingMode rounding
) {
888 unsigned int precision
= get_precision
<T
>(ulp_tolerance
);
889 MPFRNumber mpfr_result
= unary_operation_two_outputs(op
, input
, mpfrIntResult
,
890 precision
, rounding
);
891 double ulp
= mpfr_result
.ulp(libc_result
.f
);
893 if (mpfrIntResult
!= libc_result
.i
)
896 return (ulp
<= ulp_tolerance
);
899 template bool compare_unary_operation_two_outputs
<float>(
900 Operation
, float, const BinaryOutput
<float> &, double, RoundingMode
);
901 template bool compare_unary_operation_two_outputs
<double>(
902 Operation
, double, const BinaryOutput
<double> &, double, RoundingMode
);
903 template bool compare_unary_operation_two_outputs
<long double>(
904 Operation
, long double, const BinaryOutput
<long double> &, double,
907 template <typename T
>
908 bool compare_binary_operation_two_outputs(Operation op
,
909 const BinaryInput
<T
> &input
,
910 const BinaryOutput
<T
> &libc_result
,
911 double ulp_tolerance
,
912 RoundingMode rounding
) {
914 unsigned int precision
= get_precision
<T
>(ulp_tolerance
);
915 MPFRNumber mpfr_result
= binary_operation_two_outputs(
916 op
, input
.x
, input
.y
, mpfrIntResult
, precision
, rounding
);
917 double ulp
= mpfr_result
.ulp(libc_result
.f
);
919 if (mpfrIntResult
!= libc_result
.i
) {
920 if (op
== Operation::RemQuo
) {
921 if ((0x7 & mpfrIntResult
) != (0x7 & libc_result
.i
))
928 return (ulp
<= ulp_tolerance
);
931 template bool compare_binary_operation_two_outputs
<float>(
932 Operation
, const BinaryInput
<float> &, const BinaryOutput
<float> &, double,
934 template bool compare_binary_operation_two_outputs
<double>(
935 Operation
, const BinaryInput
<double> &, const BinaryOutput
<double> &,
936 double, RoundingMode
);
937 template bool compare_binary_operation_two_outputs
<long double>(
938 Operation
, const BinaryInput
<long double> &,
939 const BinaryOutput
<long double> &, double, RoundingMode
);
941 template <typename T
>
942 bool compare_binary_operation_one_output(Operation op
,
943 const BinaryInput
<T
> &input
,
944 T libc_result
, double ulp_tolerance
,
945 RoundingMode rounding
) {
946 unsigned int precision
= get_precision
<T
>(ulp_tolerance
);
947 MPFRNumber mpfr_result
=
948 binary_operation_one_output(op
, input
.x
, input
.y
, precision
, rounding
);
949 double ulp
= mpfr_result
.ulp(libc_result
);
951 return (ulp
<= ulp_tolerance
);
954 template bool compare_binary_operation_one_output
<float>(
955 Operation
, const BinaryInput
<float> &, float, double, RoundingMode
);
956 template bool compare_binary_operation_one_output
<double>(
957 Operation
, const BinaryInput
<double> &, double, double, RoundingMode
);
958 template bool compare_binary_operation_one_output
<long double>(
959 Operation
, const BinaryInput
<long double> &, long double, double,
962 template <typename T
>
963 bool compare_ternary_operation_one_output(Operation op
,
964 const TernaryInput
<T
> &input
,
965 T libc_result
, double ulp_tolerance
,
966 RoundingMode rounding
) {
967 unsigned int precision
= get_precision
<T
>(ulp_tolerance
);
968 MPFRNumber mpfr_result
= ternary_operation_one_output(
969 op
, input
.x
, input
.y
, input
.z
, precision
, rounding
);
970 double ulp
= mpfr_result
.ulp(libc_result
);
972 return (ulp
<= ulp_tolerance
);
975 template bool compare_ternary_operation_one_output
<float>(
976 Operation
, const TernaryInput
<float> &, float, double, RoundingMode
);
977 template bool compare_ternary_operation_one_output
<double>(
978 Operation
, const TernaryInput
<double> &, double, double, RoundingMode
);
979 template bool compare_ternary_operation_one_output
<long double>(
980 Operation
, const TernaryInput
<long double> &, long double, double,
983 } // namespace internal
985 template <typename T
> bool round_to_long(T x
, long &result
) {
987 return mpfr
.round_to_long(result
);
990 template bool round_to_long
<float>(float, long &);
991 template bool round_to_long
<double>(double, long &);
992 template bool round_to_long
<long double>(long double, long &);
994 template <typename T
> bool round_to_long(T x
, RoundingMode mode
, long &result
) {
996 return mpfr
.round_to_long(get_mpfr_rounding_mode(mode
), result
);
999 template bool round_to_long
<float>(float, RoundingMode
, long &);
1000 template bool round_to_long
<double>(double, RoundingMode
, long &);
1001 template bool round_to_long
<long double>(long double, RoundingMode
, long &);
1003 template <typename T
> T
round(T x
, RoundingMode mode
) {
1005 MPFRNumber result
= mpfr
.rint(get_mpfr_rounding_mode(mode
));
1006 return result
.as
<T
>();
1009 template float round
<float>(float, RoundingMode
);
1010 template double round
<double>(double, RoundingMode
);
1011 template long double round
<long double>(long double, RoundingMode
);
1014 } // namespace testing
1015 } // namespace LIBC_NAMESPACE