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 &quotient) {
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