1 //===-- Utils which wrap MPFR ---------------------------------------------===//
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 #include "MPFRUtils.h"
10
11 #include "src/__support/CPP/StringView.h"
12 #include "src/__support/FPUtil/FPBits.h"
13 #include "src/__support/FPUtil/PlatformDefs.h"
14 #include "utils/UnitTest/FPMatcher.h"
15
16 #include <cmath>
17 #include <fenv.h>
18 #include <memory>
19 #include <sstream>
20 #include <stdint.h>
21 #include <string>
22
23 #ifdef CUSTOM_MPFR_INCLUDER
24 // Some downstream repos are monoliths carrying MPFR sources in their third
25 // party directory. In such repos, including the MPFR header as
26 // `#include <mpfr.h>` is either disallowed or not possible. If that is the
27 // case, a file named `CustomMPFRIncluder.h` should be added through which the
28 // MPFR header can be included in manner allowed in that repo.
29 #include "CustomMPFRIncluder.h"
30 #else
31 #include <mpfr.h>
32 #endif
33
34 template <typename T> using FPBits = __llvm_libc::fputil::FPBits<T>;
35
36 namespace __llvm_libc {
37 namespace testing {
38 namespace mpfr {
39
40 template <typename T> struct Precision;
41
42 template <> struct Precision<float> {
43 static constexpr unsigned int VALUE = 24;
44 };
45
46 template <> struct Precision<double> {
47 static constexpr unsigned int VALUE = 53;
48 };
49
50 #if defined(LONG_DOUBLE_IS_DOUBLE)
51 template <> struct Precision<long double> {
52 static constexpr unsigned int VALUE = 53;
53 };
54 #elif defined(SPECIAL_X86_LONG_DOUBLE)
55 template <> struct Precision<long double> {
56 static constexpr unsigned int VALUE = 64;
57 };
58 #else
59 template <> struct Precision<long double> {
60 static constexpr unsigned int VALUE = 113;
61 };
62 #endif
63
64 // A precision value which allows sufficiently large additional
65 // precision compared to the floating point precision.
66 template <typename T> struct ExtraPrecision;
67
68 template <> struct ExtraPrecision<float> {
69 static constexpr unsigned int VALUE = 128;
70 };
71
72 template <> struct ExtraPrecision<double> {
73 static constexpr unsigned int VALUE = 256;
74 };
75
76 template <> struct ExtraPrecision<long double> {
77 static constexpr unsigned int VALUE = 256;
78 };
79
80 // If the ulp tolerance is less than or equal to 0.5, we would check that the
81 // result is rounded correctly with respect to the rounding mode by using the
82 // same precision as the inputs.
83 template <typename T>
get_precision(double ulp_tolerance)84 static inline unsigned int get_precision(double ulp_tolerance) {
85 if (ulp_tolerance <= 0.5) {
86 return Precision<T>::VALUE;
87 } else {
88 return ExtraPrecision<T>::VALUE;
89 }
90 }
91
get_mpfr_rounding_mode(RoundingMode mode)92 static inline mpfr_rnd_t get_mpfr_rounding_mode(RoundingMode mode) {
93 switch (mode) {
94 case RoundingMode::Upward:
95 return MPFR_RNDU;
96 break;
97 case RoundingMode::Downward:
98 return MPFR_RNDD;
99 break;
100 case RoundingMode::TowardZero:
101 return MPFR_RNDZ;
102 break;
103 case RoundingMode::Nearest:
104 return MPFR_RNDN;
105 break;
106 }
107 }
108
109 class MPFRNumber {
110 unsigned int mpfr_precision;
111 mpfr_rnd_t mpfr_rounding;
112
113 mpfr_t value;
114
115 public:
MPFRNumber()116 MPFRNumber() : mpfr_precision(256), mpfr_rounding(MPFR_RNDN) {
117 mpfr_init2(value, mpfr_precision);
118 }
119
120 // We use explicit EnableIf specializations to disallow implicit
121 // conversions. Implicit conversions can potentially lead to loss of
122 // precision.
123 template <typename XType,
124 cpp::EnableIfType<cpp::IsSame<float, XType>::Value, int> = 0>
MPFRNumber(XType x,int precision=ExtraPrecision<XType>::VALUE,RoundingMode rounding=RoundingMode::Nearest)125 explicit MPFRNumber(XType x, int precision = ExtraPrecision<XType>::VALUE,
126 RoundingMode rounding = RoundingMode::Nearest)
127 : mpfr_precision(precision),
128 mpfr_rounding(get_mpfr_rounding_mode(rounding)) {
129 mpfr_init2(value, mpfr_precision);
130 mpfr_set_flt(value, x, mpfr_rounding);
131 }
132
133 template <typename XType,
134 cpp::EnableIfType<cpp::IsSame<double, XType>::Value, int> = 0>
MPFRNumber(XType x,int precision=ExtraPrecision<XType>::VALUE,RoundingMode rounding=RoundingMode::Nearest)135 explicit MPFRNumber(XType x, int precision = ExtraPrecision<XType>::VALUE,
136 RoundingMode rounding = RoundingMode::Nearest)
137 : mpfr_precision(precision),
138 mpfr_rounding(get_mpfr_rounding_mode(rounding)) {
139 mpfr_init2(value, mpfr_precision);
140 mpfr_set_d(value, x, mpfr_rounding);
141 }
142
143 template <typename XType,
144 cpp::EnableIfType<cpp::IsSame<long double, XType>::Value, int> = 0>
MPFRNumber(XType x,int precision=ExtraPrecision<XType>::VALUE,RoundingMode rounding=RoundingMode::Nearest)145 explicit MPFRNumber(XType x, int precision = ExtraPrecision<XType>::VALUE,
146 RoundingMode rounding = RoundingMode::Nearest)
147 : mpfr_precision(precision),
148 mpfr_rounding(get_mpfr_rounding_mode(rounding)) {
149 mpfr_init2(value, mpfr_precision);
150 mpfr_set_ld(value, x, mpfr_rounding);
151 }
152
153 template <typename XType,
154 cpp::EnableIfType<cpp::IsIntegral<XType>::Value, int> = 0>
MPFRNumber(XType x,int precision=ExtraPrecision<float>::VALUE,RoundingMode rounding=RoundingMode::Nearest)155 explicit MPFRNumber(XType x, int precision = ExtraPrecision<float>::VALUE,
156 RoundingMode rounding = RoundingMode::Nearest)
157 : mpfr_precision(precision),
158 mpfr_rounding(get_mpfr_rounding_mode(rounding)) {
159 mpfr_init2(value, mpfr_precision);
160 mpfr_set_sj(value, x, mpfr_rounding);
161 }
162
MPFRNumber(const MPFRNumber & other)163 MPFRNumber(const MPFRNumber &other)
164 : mpfr_precision(other.mpfr_precision),
165 mpfr_rounding(other.mpfr_rounding) {
166 mpfr_init2(value, mpfr_precision);
167 mpfr_set(value, other.value, mpfr_rounding);
168 }
169
~MPFRNumber()170 ~MPFRNumber() { mpfr_clear(value); }
171
operator =(const MPFRNumber & rhs)172 MPFRNumber &operator=(const MPFRNumber &rhs) {
173 mpfr_precision = rhs.mpfr_precision;
174 mpfr_rounding = rhs.mpfr_rounding;
175 mpfr_set(value, rhs.value, mpfr_rounding);
176 return *this;
177 }
178
abs() const179 MPFRNumber abs() const {
180 MPFRNumber result(*this);
181 mpfr_abs(result.value, value, mpfr_rounding);
182 return result;
183 }
184
ceil() const185 MPFRNumber ceil() const {
186 MPFRNumber result(*this);
187 mpfr_ceil(result.value, value);
188 return result;
189 }
190
cos() const191 MPFRNumber cos() const {
192 MPFRNumber result(*this);
193 mpfr_cos(result.value, value, mpfr_rounding);
194 return result;
195 }
196
exp() const197 MPFRNumber exp() const {
198 MPFRNumber result(*this);
199 mpfr_exp(result.value, value, mpfr_rounding);
200 return result;
201 }
202
exp2() const203 MPFRNumber exp2() const {
204 MPFRNumber result(*this);
205 mpfr_exp2(result.value, value, mpfr_rounding);
206 return result;
207 }
208
expm1() const209 MPFRNumber expm1() const {
210 MPFRNumber result(*this);
211 mpfr_expm1(result.value, value, mpfr_rounding);
212 return result;
213 }
214
floor() const215 MPFRNumber floor() const {
216 MPFRNumber result(*this);
217 mpfr_floor(result.value, value);
218 return result;
219 }
220
fmod(const MPFRNumber & b)221 MPFRNumber fmod(const MPFRNumber &b) {
222 MPFRNumber result(*this);
223 mpfr_fmod(result.value, value, b.value, mpfr_rounding);
224 return result;
225 }
226
frexp(int & exp)227 MPFRNumber frexp(int &exp) {
228 MPFRNumber result(*this);
229 mpfr_exp_t resultExp;
230 mpfr_frexp(&resultExp, result.value, value, mpfr_rounding);
231 exp = resultExp;
232 return result;
233 }
234
hypot(const MPFRNumber & b)235 MPFRNumber hypot(const MPFRNumber &b) {
236 MPFRNumber result(*this);
237 mpfr_hypot(result.value, value, b.value, mpfr_rounding);
238 return result;
239 }
240
log() const241 MPFRNumber log() const {
242 MPFRNumber result(*this);
243 mpfr_log(result.value, value, mpfr_rounding);
244 return result;
245 }
246
log2() const247 MPFRNumber log2() const {
248 MPFRNumber result(*this);
249 mpfr_log2(result.value, value, mpfr_rounding);
250 return result;
251 }
252
log10() const253 MPFRNumber log10() const {
254 MPFRNumber result(*this);
255 mpfr_log10(result.value, value, mpfr_rounding);
256 return result;
257 }
258
log1p() const259 MPFRNumber log1p() const {
260 MPFRNumber result(*this);
261 mpfr_log1p(result.value, value, mpfr_rounding);
262 return result;
263 }
264
remquo(const MPFRNumber & divisor,int & quotient)265 MPFRNumber remquo(const MPFRNumber &divisor, int "ient) {
266 MPFRNumber remainder(*this);
267 long q;
268 mpfr_remquo(remainder.value, &q, value, divisor.value, mpfr_rounding);
269 quotient = q;
270 return remainder;
271 }
272
round() const273 MPFRNumber round() const {
274 MPFRNumber result(*this);
275 mpfr_round(result.value, value);
276 return result;
277 }
278
round_to_long(long & result) const279 bool round_to_long(long &result) const {
280 // We first calculate the rounded value. This way, when converting
281 // to long using mpfr_get_si, the rounding direction of MPFR_RNDN
282 // (or any other rounding mode), does not have an influence.
283 MPFRNumber roundedValue = round();
284 mpfr_clear_erangeflag();
285 result = mpfr_get_si(roundedValue.value, MPFR_RNDN);
286 return mpfr_erangeflag_p();
287 }
288
round_to_long(mpfr_rnd_t rnd,long & result) const289 bool round_to_long(mpfr_rnd_t rnd, long &result) const {
290 MPFRNumber rint_result(*this);
291 mpfr_rint(rint_result.value, value, rnd);
292 return rint_result.round_to_long(result);
293 }
294
rint(mpfr_rnd_t rnd) const295 MPFRNumber rint(mpfr_rnd_t rnd) const {
296 MPFRNumber result(*this);
297 mpfr_rint(result.value, value, rnd);
298 return result;
299 }
300
mod_2pi() const301 MPFRNumber mod_2pi() const {
302 MPFRNumber result(0.0, 1280);
303 MPFRNumber _2pi(0.0, 1280);
304 mpfr_const_pi(_2pi.value, MPFR_RNDN);
305 mpfr_mul_si(_2pi.value, _2pi.value, 2, MPFR_RNDN);
306 mpfr_fmod(result.value, value, _2pi.value, MPFR_RNDN);
307 return result;
308 }
309
mod_pi_over_2() const310 MPFRNumber mod_pi_over_2() const {
311 MPFRNumber result(0.0, 1280);
312 MPFRNumber pi_over_2(0.0, 1280);
313 mpfr_const_pi(pi_over_2.value, MPFR_RNDN);
314 mpfr_mul_d(pi_over_2.value, pi_over_2.value, 0.5, MPFR_RNDN);
315 mpfr_fmod(result.value, value, pi_over_2.value, MPFR_RNDN);
316 return result;
317 }
318
mod_pi_over_4() const319 MPFRNumber mod_pi_over_4() const {
320 MPFRNumber result(0.0, 1280);
321 MPFRNumber pi_over_4(0.0, 1280);
322 mpfr_const_pi(pi_over_4.value, MPFR_RNDN);
323 mpfr_mul_d(pi_over_4.value, pi_over_4.value, 0.25, MPFR_RNDN);
324 mpfr_fmod(result.value, value, pi_over_4.value, MPFR_RNDN);
325 return result;
326 }
327
sin() const328 MPFRNumber sin() const {
329 MPFRNumber result(*this);
330 mpfr_sin(result.value, value, mpfr_rounding);
331 return result;
332 }
333
sqrt() const334 MPFRNumber sqrt() const {
335 MPFRNumber result(*this);
336 mpfr_sqrt(result.value, value, mpfr_rounding);
337 return result;
338 }
339
tan() const340 MPFRNumber tan() const {
341 MPFRNumber result(*this);
342 mpfr_tan(result.value, value, mpfr_rounding);
343 return result;
344 }
345
trunc() const346 MPFRNumber trunc() const {
347 MPFRNumber result(*this);
348 mpfr_trunc(result.value, value);
349 return result;
350 }
351
fma(const MPFRNumber & b,const MPFRNumber & c)352 MPFRNumber fma(const MPFRNumber &b, const MPFRNumber &c) {
353 MPFRNumber result(*this);
354 mpfr_fma(result.value, value, b.value, c.value, mpfr_rounding);
355 return result;
356 }
357
str() const358 std::string str() const {
359 // 200 bytes should be more than sufficient to hold a 100-digit number
360 // plus additional bytes for the decimal point, '-' sign etc.
361 constexpr size_t printBufSize = 200;
362 char buffer[printBufSize];
363 mpfr_snprintf(buffer, printBufSize, "%100.50Rf", value);
364 cpp::StringView view(buffer);
365 view = view.trim(' ');
366 return std::string(view.data());
367 }
368
369 // These functions are useful for debugging.
370 template <typename T> T as() const;
371
dump(const char * msg) const372 void dump(const char *msg) const { mpfr_printf("%s%.128Rf\n", msg, value); }
373
374 // Return the ULP (units-in-the-last-place) difference between the
375 // stored MPFR and a floating point number.
376 //
377 // We define ULP difference as follows:
378 // If exponents of this value and the |input| are same, then:
379 // ULP(this_value, input) = abs(this_value - input) / eps(input)
380 // else:
381 // max = max(abs(this_value), abs(input))
382 // min = min(abs(this_value), abs(input))
383 // maxExponent = exponent(max)
384 // ULP(this_value, input) = (max - 2^maxExponent) / eps(max) +
385 // (2^maxExponent - min) / eps(min)
386 //
387 // Remarks:
388 // 1. A ULP of 0.0 will imply that the value is correctly rounded.
389 // 2. We expect that this value and the value to be compared (the [input]
390 // argument) are reasonable close, and we will provide an upper bound
391 // of ULP value for testing. Morever, most of the fractional parts of
392 // ULP value do not matter much, so using double as the return type
393 // should be good enough.
394 // 3. For close enough values (values which don't diff in their exponent by
395 // not more than 1), a ULP difference of N indicates a bit distance
396 // of N between this number and [input].
397 // 4. A values of +0.0 and -0.0 are treated as equal.
398 template <typename T>
ulp(T input)399 cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, double> ulp(T input) {
400 T thisAsT = as<T>();
401 if (thisAsT == input)
402 return T(0.0);
403
404 int thisExponent = fputil::FPBits<T>(thisAsT).get_exponent();
405 int inputExponent = fputil::FPBits<T>(input).get_exponent();
406 // Adjust the exponents for denormal numbers.
407 if (fputil::FPBits<T>(thisAsT).get_unbiased_exponent() == 0)
408 ++thisExponent;
409 if (fputil::FPBits<T>(input).get_unbiased_exponent() == 0)
410 ++inputExponent;
411
412 if (thisAsT * input < 0 || thisExponent == inputExponent) {
413 MPFRNumber inputMPFR(input);
414 mpfr_sub(inputMPFR.value, value, inputMPFR.value, MPFR_RNDN);
415 mpfr_abs(inputMPFR.value, inputMPFR.value, MPFR_RNDN);
416 mpfr_mul_2si(inputMPFR.value, inputMPFR.value,
417 -thisExponent + int(fputil::MantissaWidth<T>::VALUE),
418 MPFR_RNDN);
419 return inputMPFR.as<double>();
420 }
421
422 // If the control reaches here, it means that this number and input are
423 // of the same sign but different exponent. In such a case, ULP error is
424 // calculated as sum of two parts.
425 thisAsT = std::abs(thisAsT);
426 input = std::abs(input);
427 T min = thisAsT > input ? input : thisAsT;
428 T max = thisAsT > input ? thisAsT : input;
429 int minExponent = fputil::FPBits<T>(min).get_exponent();
430 int maxExponent = fputil::FPBits<T>(max).get_exponent();
431 // Adjust the exponents for denormal numbers.
432 if (fputil::FPBits<T>(min).get_unbiased_exponent() == 0)
433 ++minExponent;
434 if (fputil::FPBits<T>(max).get_unbiased_exponent() == 0)
435 ++maxExponent;
436
437 MPFRNumber minMPFR(min);
438 MPFRNumber maxMPFR(max);
439
440 MPFRNumber pivot(uint32_t(1));
441 mpfr_mul_2si(pivot.value, pivot.value, maxExponent, MPFR_RNDN);
442
443 mpfr_sub(minMPFR.value, pivot.value, minMPFR.value, MPFR_RNDN);
444 mpfr_mul_2si(minMPFR.value, minMPFR.value,
445 -minExponent + int(fputil::MantissaWidth<T>::VALUE),
446 MPFR_RNDN);
447
448 mpfr_sub(maxMPFR.value, maxMPFR.value, pivot.value, MPFR_RNDN);
449 mpfr_mul_2si(maxMPFR.value, maxMPFR.value,
450 -maxExponent + int(fputil::MantissaWidth<T>::VALUE),
451 MPFR_RNDN);
452
453 mpfr_add(minMPFR.value, minMPFR.value, maxMPFR.value, MPFR_RNDN);
454 return minMPFR.as<double>();
455 }
456 };
457
as() const458 template <> float MPFRNumber::as<float>() const {
459 return mpfr_get_flt(value, mpfr_rounding);
460 }
461
as() const462 template <> double MPFRNumber::as<double>() const {
463 return mpfr_get_d(value, mpfr_rounding);
464 }
465
as() const466 template <> long double MPFRNumber::as<long double>() const {
467 return mpfr_get_ld(value, mpfr_rounding);
468 }
469
470 namespace internal {
471
472 template <typename InputType>
473 cpp::EnableIfType<cpp::IsFloatingPointType<InputType>::Value, MPFRNumber>
unary_operation(Operation op,InputType input,unsigned int precision,RoundingMode rounding)474 unary_operation(Operation op, InputType input, unsigned int precision,
475 RoundingMode rounding) {
476 MPFRNumber mpfrInput(input, precision, rounding);
477 switch (op) {
478 case Operation::Abs:
479 return mpfrInput.abs();
480 case Operation::Ceil:
481 return mpfrInput.ceil();
482 case Operation::Cos:
483 return mpfrInput.cos();
484 case Operation::Exp:
485 return mpfrInput.exp();
486 case Operation::Exp2:
487 return mpfrInput.exp2();
488 case Operation::Expm1:
489 return mpfrInput.expm1();
490 case Operation::Floor:
491 return mpfrInput.floor();
492 case Operation::Log:
493 return mpfrInput.log();
494 case Operation::Log2:
495 return mpfrInput.log2();
496 case Operation::Log10:
497 return mpfrInput.log10();
498 case Operation::Log1p:
499 return mpfrInput.log1p();
500 case Operation::Mod2PI:
501 return mpfrInput.mod_2pi();
502 case Operation::ModPIOver2:
503 return mpfrInput.mod_pi_over_2();
504 case Operation::ModPIOver4:
505 return mpfrInput.mod_pi_over_4();
506 case Operation::Round:
507 return mpfrInput.round();
508 case Operation::Sin:
509 return mpfrInput.sin();
510 case Operation::Sqrt:
511 return mpfrInput.sqrt();
512 case Operation::Tan:
513 return mpfrInput.tan();
514 case Operation::Trunc:
515 return mpfrInput.trunc();
516 default:
517 __builtin_unreachable();
518 }
519 }
520
521 template <typename InputType>
522 cpp::EnableIfType<cpp::IsFloatingPointType<InputType>::Value, MPFRNumber>
unary_operation_two_outputs(Operation op,InputType input,int & output,unsigned int precision,RoundingMode rounding)523 unary_operation_two_outputs(Operation op, InputType input, int &output,
524 unsigned int precision, RoundingMode rounding) {
525 MPFRNumber mpfrInput(input, precision, rounding);
526 switch (op) {
527 case Operation::Frexp:
528 return mpfrInput.frexp(output);
529 default:
530 __builtin_unreachable();
531 }
532 }
533
534 template <typename InputType>
535 cpp::EnableIfType<cpp::IsFloatingPointType<InputType>::Value, MPFRNumber>
binary_operation_one_output(Operation op,InputType x,InputType y,unsigned int precision,RoundingMode rounding)536 binary_operation_one_output(Operation op, InputType x, InputType y,
537 unsigned int precision, RoundingMode rounding) {
538 MPFRNumber inputX(x, precision, rounding);
539 MPFRNumber inputY(y, precision, rounding);
540 switch (op) {
541 case Operation::Fmod:
542 return inputX.fmod(inputY);
543 case Operation::Hypot:
544 return inputX.hypot(inputY);
545 default:
546 __builtin_unreachable();
547 }
548 }
549
550 template <typename InputType>
551 cpp::EnableIfType<cpp::IsFloatingPointType<InputType>::Value, MPFRNumber>
binary_operation_two_outputs(Operation op,InputType x,InputType y,int & output,unsigned int precision,RoundingMode rounding)552 binary_operation_two_outputs(Operation op, InputType x, InputType y,
553 int &output, unsigned int precision,
554 RoundingMode rounding) {
555 MPFRNumber inputX(x, precision, rounding);
556 MPFRNumber inputY(y, precision, rounding);
557 switch (op) {
558 case Operation::RemQuo:
559 return inputX.remquo(inputY, output);
560 default:
561 __builtin_unreachable();
562 }
563 }
564
565 template <typename InputType>
566 cpp::EnableIfType<cpp::IsFloatingPointType<InputType>::Value, MPFRNumber>
ternary_operation_one_output(Operation op,InputType x,InputType y,InputType z,unsigned int precision,RoundingMode rounding)567 ternary_operation_one_output(Operation op, InputType x, InputType y,
568 InputType z, unsigned int precision,
569 RoundingMode rounding) {
570 // For FMA function, we just need to compare with the mpfr_fma with the same
571 // precision as InputType. Using higher precision as the intermediate results
572 // to compare might incorrectly fail due to double-rounding errors.
573 MPFRNumber inputX(x, precision, rounding);
574 MPFRNumber inputY(y, precision, rounding);
575 MPFRNumber inputZ(z, precision, rounding);
576 switch (op) {
577 case Operation::Fma:
578 return inputX.fma(inputY, inputZ);
579 default:
580 __builtin_unreachable();
581 }
582 }
583
584 // Remark: For all the explain_*_error functions, we will use std::stringstream
585 // to build the complete error messages before sending it to the outstream `OS`
586 // once at the end. This will stop the error messages from interleaving when
587 // the tests are running concurrently.
588 template <typename T>
explain_unary_operation_single_output_error(Operation op,T input,T matchValue,double ulp_tolerance,RoundingMode rounding,testutils::StreamWrapper & OS)589 void explain_unary_operation_single_output_error(Operation op, T input,
590 T matchValue,
591 double ulp_tolerance,
592 RoundingMode rounding,
593 testutils::StreamWrapper &OS) {
594 unsigned int precision = get_precision<T>(ulp_tolerance);
595 MPFRNumber mpfrInput(input, precision);
596 MPFRNumber mpfr_result;
597 mpfr_result = unary_operation(op, input, precision, rounding);
598 MPFRNumber mpfrMatchValue(matchValue);
599 std::stringstream ss;
600 ss << "Match value not within tolerance value of MPFR result:\n"
601 << " Input decimal: " << mpfrInput.str() << '\n';
602 __llvm_libc::fputil::testing::describeValue(" Input bits: ", input, ss);
603 ss << '\n' << " Match decimal: " << mpfrMatchValue.str() << '\n';
604 __llvm_libc::fputil::testing::describeValue(" Match bits: ", matchValue,
605 ss);
606 ss << '\n' << " MPFR result: " << mpfr_result.str() << '\n';
607 __llvm_libc::fputil::testing::describeValue(
608 " MPFR rounded: ", mpfr_result.as<T>(), ss);
609 ss << '\n';
610 ss << " ULP error: " << std::to_string(mpfr_result.ulp(matchValue))
611 << '\n';
612 OS << ss.str();
613 }
614
615 template void
616 explain_unary_operation_single_output_error<float>(Operation op, float, float,
617 double, RoundingMode,
618 testutils::StreamWrapper &);
619 template void explain_unary_operation_single_output_error<double>(
620 Operation op, double, double, double, RoundingMode,
621 testutils::StreamWrapper &);
622 template void explain_unary_operation_single_output_error<long double>(
623 Operation op, long double, long double, double, RoundingMode,
624 testutils::StreamWrapper &);
625
626 template <typename T>
explain_unary_operation_two_outputs_error(Operation op,T input,const BinaryOutput<T> & libc_result,double ulp_tolerance,RoundingMode rounding,testutils::StreamWrapper & OS)627 void explain_unary_operation_two_outputs_error(
628 Operation op, T input, const BinaryOutput<T> &libc_result,
629 double ulp_tolerance, RoundingMode rounding, testutils::StreamWrapper &OS) {
630 unsigned int precision = get_precision<T>(ulp_tolerance);
631 MPFRNumber mpfrInput(input, precision);
632 int mpfrIntResult;
633 MPFRNumber mpfr_result = unary_operation_two_outputs(op, input, mpfrIntResult,
634 precision, rounding);
635 std::stringstream ss;
636
637 if (mpfrIntResult != libc_result.i) {
638 ss << "MPFR integral result: " << mpfrIntResult << '\n'
639 << "Libc integral result: " << libc_result.i << '\n';
640 } else {
641 ss << "Integral result from libc matches integral result from MPFR.\n";
642 }
643
644 MPFRNumber mpfrMatchValue(libc_result.f);
645 ss << "Libc floating point result is not within tolerance value of the MPFR "
646 << "result.\n\n";
647
648 ss << " Input decimal: " << mpfrInput.str() << "\n\n";
649
650 ss << "Libc floating point value: " << mpfrMatchValue.str() << '\n';
651 __llvm_libc::fputil::testing::describeValue(
652 " Libc floating point bits: ", libc_result.f, ss);
653 ss << "\n\n";
654
655 ss << " MPFR result: " << mpfr_result.str() << '\n';
656 __llvm_libc::fputil::testing::describeValue(
657 " MPFR rounded: ", mpfr_result.as<T>(), ss);
658 ss << '\n'
659 << " ULP error: "
660 << std::to_string(mpfr_result.ulp(libc_result.f)) << '\n';
661 OS << ss.str();
662 }
663
664 template void explain_unary_operation_two_outputs_error<float>(
665 Operation, float, const BinaryOutput<float> &, double, RoundingMode,
666 testutils::StreamWrapper &);
667 template void explain_unary_operation_two_outputs_error<double>(
668 Operation, double, const BinaryOutput<double> &, double, RoundingMode,
669 testutils::StreamWrapper &);
670 template void explain_unary_operation_two_outputs_error<long double>(
671 Operation, long double, const BinaryOutput<long double> &, double,
672 RoundingMode, testutils::StreamWrapper &);
673
674 template <typename T>
explain_binary_operation_two_outputs_error(Operation op,const BinaryInput<T> & input,const BinaryOutput<T> & libc_result,double ulp_tolerance,RoundingMode rounding,testutils::StreamWrapper & OS)675 void explain_binary_operation_two_outputs_error(
676 Operation op, const BinaryInput<T> &input,
677 const BinaryOutput<T> &libc_result, double ulp_tolerance,
678 RoundingMode rounding, testutils::StreamWrapper &OS) {
679 unsigned int precision = get_precision<T>(ulp_tolerance);
680 MPFRNumber mpfrX(input.x, precision);
681 MPFRNumber mpfrY(input.y, precision);
682 int mpfrIntResult;
683 MPFRNumber mpfr_result = binary_operation_two_outputs(
684 op, input.x, input.y, mpfrIntResult, precision, rounding);
685 MPFRNumber mpfrMatchValue(libc_result.f);
686 std::stringstream ss;
687
688 ss << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n'
689 << "MPFR integral result: " << mpfrIntResult << '\n'
690 << "Libc integral result: " << libc_result.i << '\n'
691 << "Libc floating point result: " << mpfrMatchValue.str() << '\n'
692 << " MPFR result: " << mpfr_result.str() << '\n';
693 __llvm_libc::fputil::testing::describeValue(
694 "Libc floating point result bits: ", libc_result.f, ss);
695 __llvm_libc::fputil::testing::describeValue(
696 " MPFR rounded bits: ", mpfr_result.as<T>(), ss);
697 ss << "ULP error: " << std::to_string(mpfr_result.ulp(libc_result.f)) << '\n';
698 OS << ss.str();
699 }
700
701 template void explain_binary_operation_two_outputs_error<float>(
702 Operation, const BinaryInput<float> &, const BinaryOutput<float> &, double,
703 RoundingMode, testutils::StreamWrapper &);
704 template void explain_binary_operation_two_outputs_error<double>(
705 Operation, const BinaryInput<double> &, const BinaryOutput<double> &,
706 double, RoundingMode, testutils::StreamWrapper &);
707 template void explain_binary_operation_two_outputs_error<long double>(
708 Operation, const BinaryInput<long double> &,
709 const BinaryOutput<long double> &, double, RoundingMode,
710 testutils::StreamWrapper &);
711
712 template <typename T>
explain_binary_operation_one_output_error(Operation op,const BinaryInput<T> & input,T libc_result,double ulp_tolerance,RoundingMode rounding,testutils::StreamWrapper & OS)713 void explain_binary_operation_one_output_error(
714 Operation op, const BinaryInput<T> &input, T libc_result,
715 double ulp_tolerance, RoundingMode rounding, testutils::StreamWrapper &OS) {
716 unsigned int precision = get_precision<T>(ulp_tolerance);
717 MPFRNumber mpfrX(input.x, precision);
718 MPFRNumber mpfrY(input.y, precision);
719 FPBits<T> xbits(input.x);
720 FPBits<T> ybits(input.y);
721 MPFRNumber mpfr_result =
722 binary_operation_one_output(op, input.x, input.y, precision, rounding);
723 MPFRNumber mpfrMatchValue(libc_result);
724 std::stringstream ss;
725
726 ss << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n';
727 __llvm_libc::fputil::testing::describeValue("First input bits: ", input.x,
728 ss);
729 __llvm_libc::fputil::testing::describeValue("Second input bits: ", input.y,
730 ss);
731
732 ss << "Libc result: " << mpfrMatchValue.str() << '\n'
733 << "MPFR result: " << mpfr_result.str() << '\n';
734 __llvm_libc::fputil::testing::describeValue(
735 "Libc floating point result bits: ", libc_result, ss);
736 __llvm_libc::fputil::testing::describeValue(
737 " MPFR rounded bits: ", mpfr_result.as<T>(), ss);
738 ss << "ULP error: " << std::to_string(mpfr_result.ulp(libc_result)) << '\n';
739 OS << ss.str();
740 }
741
742 template void explain_binary_operation_one_output_error<float>(
743 Operation, const BinaryInput<float> &, float, double, RoundingMode,
744 testutils::StreamWrapper &);
745 template void explain_binary_operation_one_output_error<double>(
746 Operation, const BinaryInput<double> &, double, double, RoundingMode,
747 testutils::StreamWrapper &);
748 template void explain_binary_operation_one_output_error<long double>(
749 Operation, const BinaryInput<long double> &, long double, double,
750 RoundingMode, testutils::StreamWrapper &);
751
752 template <typename T>
explain_ternary_operation_one_output_error(Operation op,const TernaryInput<T> & input,T libc_result,double ulp_tolerance,RoundingMode rounding,testutils::StreamWrapper & OS)753 void explain_ternary_operation_one_output_error(
754 Operation op, const TernaryInput<T> &input, T libc_result,
755 double ulp_tolerance, RoundingMode rounding, testutils::StreamWrapper &OS) {
756 unsigned int precision = get_precision<T>(ulp_tolerance);
757 MPFRNumber mpfrX(input.x, precision);
758 MPFRNumber mpfrY(input.y, precision);
759 MPFRNumber mpfrZ(input.z, precision);
760 FPBits<T> xbits(input.x);
761 FPBits<T> ybits(input.y);
762 FPBits<T> zbits(input.z);
763 MPFRNumber mpfr_result = ternary_operation_one_output(
764 op, input.x, input.y, input.z, precision, rounding);
765 MPFRNumber mpfrMatchValue(libc_result);
766 std::stringstream ss;
767
768 ss << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str()
769 << " z: " << mpfrZ.str() << '\n';
770 __llvm_libc::fputil::testing::describeValue("First input bits: ", input.x,
771 ss);
772 __llvm_libc::fputil::testing::describeValue("Second input bits: ", input.y,
773 ss);
774 __llvm_libc::fputil::testing::describeValue("Third input bits: ", input.z,
775 ss);
776
777 ss << "Libc result: " << mpfrMatchValue.str() << '\n'
778 << "MPFR result: " << mpfr_result.str() << '\n';
779 __llvm_libc::fputil::testing::describeValue(
780 "Libc floating point result bits: ", libc_result, ss);
781 __llvm_libc::fputil::testing::describeValue(
782 " MPFR rounded bits: ", mpfr_result.as<T>(), ss);
783 ss << "ULP error: " << std::to_string(mpfr_result.ulp(libc_result)) << '\n';
784 OS << ss.str();
785 }
786
787 template void explain_ternary_operation_one_output_error<float>(
788 Operation, const TernaryInput<float> &, float, double, RoundingMode,
789 testutils::StreamWrapper &);
790 template void explain_ternary_operation_one_output_error<double>(
791 Operation, const TernaryInput<double> &, double, double, RoundingMode,
792 testutils::StreamWrapper &);
793 template void explain_ternary_operation_one_output_error<long double>(
794 Operation, const TernaryInput<long double> &, long double, double,
795 RoundingMode, testutils::StreamWrapper &);
796
797 template <typename T>
compare_unary_operation_single_output(Operation op,T input,T libc_result,double ulp_tolerance,RoundingMode rounding)798 bool compare_unary_operation_single_output(Operation op, T input, T libc_result,
799 double ulp_tolerance,
800 RoundingMode rounding) {
801 unsigned int precision = get_precision<T>(ulp_tolerance);
802 MPFRNumber mpfr_result;
803 mpfr_result = unary_operation(op, input, precision, rounding);
804 double ulp = mpfr_result.ulp(libc_result);
805 return (ulp <= ulp_tolerance);
806 }
807
808 template bool compare_unary_operation_single_output<float>(Operation, float,
809 float, double,
810 RoundingMode);
811 template bool compare_unary_operation_single_output<double>(Operation, double,
812 double, double,
813 RoundingMode);
814 template bool compare_unary_operation_single_output<long double>(
815 Operation, long double, long double, double, RoundingMode);
816
817 template <typename T>
compare_unary_operation_two_outputs(Operation op,T input,const BinaryOutput<T> & libc_result,double ulp_tolerance,RoundingMode rounding)818 bool compare_unary_operation_two_outputs(Operation op, T input,
819 const BinaryOutput<T> &libc_result,
820 double ulp_tolerance,
821 RoundingMode rounding) {
822 int mpfrIntResult;
823 unsigned int precision = get_precision<T>(ulp_tolerance);
824 MPFRNumber mpfr_result = unary_operation_two_outputs(op, input, mpfrIntResult,
825 precision, rounding);
826 double ulp = mpfr_result.ulp(libc_result.f);
827
828 if (mpfrIntResult != libc_result.i)
829 return false;
830
831 return (ulp <= ulp_tolerance);
832 }
833
834 template bool compare_unary_operation_two_outputs<float>(
835 Operation, float, const BinaryOutput<float> &, double, RoundingMode);
836 template bool compare_unary_operation_two_outputs<double>(
837 Operation, double, const BinaryOutput<double> &, double, RoundingMode);
838 template bool compare_unary_operation_two_outputs<long double>(
839 Operation, long double, const BinaryOutput<long double> &, double,
840 RoundingMode);
841
842 template <typename T>
compare_binary_operation_two_outputs(Operation op,const BinaryInput<T> & input,const BinaryOutput<T> & libc_result,double ulp_tolerance,RoundingMode rounding)843 bool compare_binary_operation_two_outputs(Operation op,
844 const BinaryInput<T> &input,
845 const BinaryOutput<T> &libc_result,
846 double ulp_tolerance,
847 RoundingMode rounding) {
848 int mpfrIntResult;
849 unsigned int precision = get_precision<T>(ulp_tolerance);
850 MPFRNumber mpfr_result = binary_operation_two_outputs(
851 op, input.x, input.y, mpfrIntResult, precision, rounding);
852 double ulp = mpfr_result.ulp(libc_result.f);
853
854 if (mpfrIntResult != libc_result.i) {
855 if (op == Operation::RemQuo) {
856 if ((0x7 & mpfrIntResult) != (0x7 & libc_result.i))
857 return false;
858 } else {
859 return false;
860 }
861 }
862
863 return (ulp <= ulp_tolerance);
864 }
865
866 template bool compare_binary_operation_two_outputs<float>(
867 Operation, const BinaryInput<float> &, const BinaryOutput<float> &, double,
868 RoundingMode);
869 template bool compare_binary_operation_two_outputs<double>(
870 Operation, const BinaryInput<double> &, const BinaryOutput<double> &,
871 double, RoundingMode);
872 template bool compare_binary_operation_two_outputs<long double>(
873 Operation, const BinaryInput<long double> &,
874 const BinaryOutput<long double> &, double, RoundingMode);
875
876 template <typename T>
compare_binary_operation_one_output(Operation op,const BinaryInput<T> & input,T libc_result,double ulp_tolerance,RoundingMode rounding)877 bool compare_binary_operation_one_output(Operation op,
878 const BinaryInput<T> &input,
879 T libc_result, double ulp_tolerance,
880 RoundingMode rounding) {
881 unsigned int precision = get_precision<T>(ulp_tolerance);
882 MPFRNumber mpfr_result =
883 binary_operation_one_output(op, input.x, input.y, precision, rounding);
884 double ulp = mpfr_result.ulp(libc_result);
885
886 return (ulp <= ulp_tolerance);
887 }
888
889 template bool compare_binary_operation_one_output<float>(
890 Operation, const BinaryInput<float> &, float, double, RoundingMode);
891 template bool compare_binary_operation_one_output<double>(
892 Operation, const BinaryInput<double> &, double, double, RoundingMode);
893 template bool compare_binary_operation_one_output<long double>(
894 Operation, const BinaryInput<long double> &, long double, double,
895 RoundingMode);
896
897 template <typename T>
compare_ternary_operation_one_output(Operation op,const TernaryInput<T> & input,T libc_result,double ulp_tolerance,RoundingMode rounding)898 bool compare_ternary_operation_one_output(Operation op,
899 const TernaryInput<T> &input,
900 T libc_result, double ulp_tolerance,
901 RoundingMode rounding) {
902 unsigned int precision = get_precision<T>(ulp_tolerance);
903 MPFRNumber mpfr_result = ternary_operation_one_output(
904 op, input.x, input.y, input.z, precision, rounding);
905 double ulp = mpfr_result.ulp(libc_result);
906
907 return (ulp <= ulp_tolerance);
908 }
909
910 template bool compare_ternary_operation_one_output<float>(
911 Operation, const TernaryInput<float> &, float, double, RoundingMode);
912 template bool compare_ternary_operation_one_output<double>(
913 Operation, const TernaryInput<double> &, double, double, RoundingMode);
914 template bool compare_ternary_operation_one_output<long double>(
915 Operation, const TernaryInput<long double> &, long double, double,
916 RoundingMode);
917
918 } // namespace internal
919
round_to_long(T x,long & result)920 template <typename T> bool round_to_long(T x, long &result) {
921 MPFRNumber mpfr(x);
922 return mpfr.round_to_long(result);
923 }
924
925 template bool round_to_long<float>(float, long &);
926 template bool round_to_long<double>(double, long &);
927 template bool round_to_long<long double>(long double, long &);
928
round_to_long(T x,RoundingMode mode,long & result)929 template <typename T> bool round_to_long(T x, RoundingMode mode, long &result) {
930 MPFRNumber mpfr(x);
931 return mpfr.round_to_long(get_mpfr_rounding_mode(mode), result);
932 }
933
934 template bool round_to_long<float>(float, RoundingMode, long &);
935 template bool round_to_long<double>(double, RoundingMode, long &);
936 template bool round_to_long<long double>(long double, RoundingMode, long &);
937
round(T x,RoundingMode mode)938 template <typename T> T round(T x, RoundingMode mode) {
939 MPFRNumber mpfr(x);
940 MPFRNumber result = mpfr.rint(get_mpfr_rounding_mode(mode));
941 return result.as<T>();
942 }
943
944 template float round<float>(float, RoundingMode);
945 template double round<double>(double, RoundingMode);
946 template long double round<long double>(long double, RoundingMode);
947
948 } // namespace mpfr
949 } // namespace testing
950 } // namespace __llvm_libc
951