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/array.h"
12 #include "src/__support/CPP/string.h"
13 #include "src/__support/CPP/string_view.h"
14 #include "src/__support/CPP/stringstream.h"
15 #include "src/__support/FPUtil/FPBits.h"
16 #include "src/__support/FPUtil/cast.h"
17 #include "src/__support/FPUtil/fpbits_str.h"
18 #include "src/__support/macros/config.h"
19 #include "src/__support/macros/properties/types.h"
20
21 #include <stdint.h>
22
23 #include "mpfr_inc.h"
24
25 template <typename T> using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>;
26
27 namespace LIBC_NAMESPACE_DECL {
28 namespace testing {
29 namespace mpfr {
30
31 // A precision value which allows sufficiently large additional
32 // precision compared to the floating point precision.
33 template <typename T> struct ExtraPrecision;
34
35 #ifdef LIBC_TYPES_HAS_FLOAT16
36 template <> struct ExtraPrecision<float16> {
37 static constexpr unsigned int VALUE = 128;
38 };
39 #endif
40
41 template <> struct ExtraPrecision<float> {
42 static constexpr unsigned int VALUE = 128;
43 };
44
45 template <> struct ExtraPrecision<double> {
46 static constexpr unsigned int VALUE = 256;
47 };
48
49 template <> struct ExtraPrecision<long double> {
50 static constexpr unsigned int VALUE = 256;
51 };
52
53 // If the ulp tolerance is less than or equal to 0.5, we would check that the
54 // result is rounded correctly with respect to the rounding mode by using the
55 // same precision as the inputs.
56 template <typename T>
get_precision(double ulp_tolerance)57 static inline unsigned int get_precision(double ulp_tolerance) {
58 if (ulp_tolerance <= 0.5) {
59 return LIBC_NAMESPACE::fputil::FPBits<T>::FRACTION_LEN + 1;
60 } else {
61 return ExtraPrecision<T>::VALUE;
62 }
63 }
64
get_mpfr_rounding_mode(RoundingMode mode)65 static inline mpfr_rnd_t get_mpfr_rounding_mode(RoundingMode mode) {
66 switch (mode) {
67 case RoundingMode::Upward:
68 return MPFR_RNDU;
69 break;
70 case RoundingMode::Downward:
71 return MPFR_RNDD;
72 break;
73 case RoundingMode::TowardZero:
74 return MPFR_RNDZ;
75 break;
76 case RoundingMode::Nearest:
77 return MPFR_RNDN;
78 break;
79 }
80 __builtin_unreachable();
81 }
82
83 class MPFRNumber {
84 unsigned int mpfr_precision;
85 mpfr_rnd_t mpfr_rounding;
86
87 mpfr_t value;
88
89 public:
MPFRNumber()90 MPFRNumber() : mpfr_precision(256), mpfr_rounding(MPFR_RNDN) {
91 mpfr_init2(value, mpfr_precision);
92 }
93
94 // We use explicit EnableIf specializations to disallow implicit
95 // conversions. Implicit conversions can potentially lead to loss of
96 // precision. We exceptionally allow implicit conversions from float16
97 // to float, as the MPFR API does not support float16, thus requiring
98 // conversion to a higher-precision format.
99 template <typename XType,
100 cpp::enable_if_t<cpp::is_same_v<float, XType>
101 #ifdef LIBC_TYPES_HAS_FLOAT16
102 || cpp::is_same_v<float16, XType>
103 #endif
104 ,
105 int> = 0>
MPFRNumber(XType x,unsigned int precision=ExtraPrecision<XType>::VALUE,RoundingMode rounding=RoundingMode::Nearest)106 explicit MPFRNumber(XType x,
107 unsigned int precision = ExtraPrecision<XType>::VALUE,
108 RoundingMode rounding = RoundingMode::Nearest)
109 : mpfr_precision(precision),
110 mpfr_rounding(get_mpfr_rounding_mode(rounding)) {
111 mpfr_init2(value, mpfr_precision);
112 mpfr_set_flt(value, x, mpfr_rounding);
113 }
114
115 template <typename XType,
116 cpp::enable_if_t<cpp::is_same_v<double, XType>, int> = 0>
MPFRNumber(XType x,unsigned int precision=ExtraPrecision<XType>::VALUE,RoundingMode rounding=RoundingMode::Nearest)117 explicit MPFRNumber(XType x,
118 unsigned int precision = ExtraPrecision<XType>::VALUE,
119 RoundingMode rounding = RoundingMode::Nearest)
120 : mpfr_precision(precision),
121 mpfr_rounding(get_mpfr_rounding_mode(rounding)) {
122 mpfr_init2(value, mpfr_precision);
123 mpfr_set_d(value, x, mpfr_rounding);
124 }
125
126 template <typename XType,
127 cpp::enable_if_t<cpp::is_same_v<long double, XType>, int> = 0>
MPFRNumber(XType x,unsigned int precision=ExtraPrecision<XType>::VALUE,RoundingMode rounding=RoundingMode::Nearest)128 explicit MPFRNumber(XType x,
129 unsigned int precision = ExtraPrecision<XType>::VALUE,
130 RoundingMode rounding = RoundingMode::Nearest)
131 : mpfr_precision(precision),
132 mpfr_rounding(get_mpfr_rounding_mode(rounding)) {
133 mpfr_init2(value, mpfr_precision);
134 mpfr_set_ld(value, x, mpfr_rounding);
135 }
136
137 template <typename XType,
138 cpp::enable_if_t<cpp::is_integral_v<XType>, int> = 0>
MPFRNumber(XType x,unsigned int precision=ExtraPrecision<float>::VALUE,RoundingMode rounding=RoundingMode::Nearest)139 explicit MPFRNumber(XType x,
140 unsigned int precision = ExtraPrecision<float>::VALUE,
141 RoundingMode rounding = RoundingMode::Nearest)
142 : mpfr_precision(precision),
143 mpfr_rounding(get_mpfr_rounding_mode(rounding)) {
144 mpfr_init2(value, mpfr_precision);
145 mpfr_set_sj(value, x, mpfr_rounding);
146 }
147
MPFRNumber(const MPFRNumber & other)148 MPFRNumber(const MPFRNumber &other)
149 : mpfr_precision(other.mpfr_precision),
150 mpfr_rounding(other.mpfr_rounding) {
151 mpfr_init2(value, mpfr_precision);
152 mpfr_set(value, other.value, mpfr_rounding);
153 }
154
MPFRNumber(const MPFRNumber & other,unsigned int precision)155 MPFRNumber(const MPFRNumber &other, unsigned int precision)
156 : mpfr_precision(precision), mpfr_rounding(other.mpfr_rounding) {
157 mpfr_init2(value, mpfr_precision);
158 mpfr_set(value, other.value, mpfr_rounding);
159 }
160
~MPFRNumber()161 ~MPFRNumber() { mpfr_clear(value); }
162
operator =(const MPFRNumber & rhs)163 MPFRNumber &operator=(const MPFRNumber &rhs) {
164 mpfr_precision = rhs.mpfr_precision;
165 mpfr_rounding = rhs.mpfr_rounding;
166 mpfr_set(value, rhs.value, mpfr_rounding);
167 return *this;
168 }
169
is_nan() const170 bool is_nan() const { return mpfr_nan_p(value); }
171
abs() const172 MPFRNumber abs() const {
173 MPFRNumber result(*this);
174 mpfr_abs(result.value, value, mpfr_rounding);
175 return result;
176 }
177
acos() const178 MPFRNumber acos() const {
179 MPFRNumber result(*this);
180 mpfr_acos(result.value, value, mpfr_rounding);
181 return result;
182 }
183
acosh() const184 MPFRNumber acosh() const {
185 MPFRNumber result(*this);
186 mpfr_acosh(result.value, value, mpfr_rounding);
187 return result;
188 }
189
add(const MPFRNumber & b) const190 MPFRNumber add(const MPFRNumber &b) const {
191 MPFRNumber result(*this);
192 mpfr_add(result.value, value, b.value, mpfr_rounding);
193 return result;
194 }
195
asin() const196 MPFRNumber asin() const {
197 MPFRNumber result(*this);
198 mpfr_asin(result.value, value, mpfr_rounding);
199 return result;
200 }
201
asinh() const202 MPFRNumber asinh() const {
203 MPFRNumber result(*this);
204 mpfr_asinh(result.value, value, mpfr_rounding);
205 return result;
206 }
207
atan() const208 MPFRNumber atan() const {
209 MPFRNumber result(*this);
210 mpfr_atan(result.value, value, mpfr_rounding);
211 return result;
212 }
213
atan2(const MPFRNumber & b)214 MPFRNumber atan2(const MPFRNumber &b) {
215 MPFRNumber result(*this);
216 mpfr_atan2(result.value, value, b.value, mpfr_rounding);
217 return result;
218 }
219
atanh() const220 MPFRNumber atanh() const {
221 MPFRNumber result(*this);
222 mpfr_atanh(result.value, value, mpfr_rounding);
223 return result;
224 }
225
cbrt() const226 MPFRNumber cbrt() const {
227 MPFRNumber result(*this);
228 mpfr_cbrt(result.value, value, mpfr_rounding);
229 return result;
230 }
231
ceil() const232 MPFRNumber ceil() const {
233 MPFRNumber result(*this);
234 mpfr_ceil(result.value, value);
235 return result;
236 }
237
cos() const238 MPFRNumber cos() const {
239 MPFRNumber result(*this);
240 mpfr_cos(result.value, value, mpfr_rounding);
241 return result;
242 }
243
cosh() const244 MPFRNumber cosh() const {
245 MPFRNumber result(*this);
246 mpfr_cosh(result.value, value, mpfr_rounding);
247 return result;
248 }
249
cospi() const250 MPFRNumber cospi() const {
251 MPFRNumber result(*this);
252
253 #if MPFR_VERSION_MAJOR > 4 || \
254 (MPFR_VERSION_MAJOR == 4 && MPFR_VERSION_MINOR >= 2)
255 mpfr_cospi(result.value, value, mpfr_rounding);
256 return result;
257 #else
258 if (mpfr_integer_p(value)) {
259 mpz_t integer;
260 mpz_init(integer);
261 mpfr_get_z(integer, value, mpfr_rounding);
262
263 int d = mpz_tstbit(integer, 0);
264 mpfr_set_si(result.value, d ? -1 : 1, mpfr_rounding);
265 mpz_clear(integer);
266 return result;
267 }
268
269 MPFRNumber value_pi(0.0, 1280);
270 mpfr_const_pi(value_pi.value, MPFR_RNDN);
271 mpfr_mul(value_pi.value, value_pi.value, value, MPFR_RNDN);
272 mpfr_cos(result.value, value_pi.value, mpfr_rounding);
273
274 return result;
275 #endif
276 }
277
erf() const278 MPFRNumber erf() const {
279 MPFRNumber result(*this);
280 mpfr_erf(result.value, value, mpfr_rounding);
281 return result;
282 }
283
exp() const284 MPFRNumber exp() const {
285 MPFRNumber result(*this);
286 mpfr_exp(result.value, value, mpfr_rounding);
287 return result;
288 }
289
exp2() const290 MPFRNumber exp2() const {
291 MPFRNumber result(*this);
292 mpfr_exp2(result.value, value, mpfr_rounding);
293 return result;
294 }
295
exp2m1() const296 MPFRNumber exp2m1() const {
297 // TODO: Only use mpfr_exp2m1 once CI and buildbots get MPFR >= 4.2.0.
298 #if MPFR_VERSION_MAJOR > 4 || \
299 (MPFR_VERSION_MAJOR == 4 && MPFR_VERSION_MINOR >= 2)
300 MPFRNumber result(*this);
301 mpfr_exp2m1(result.value, value, mpfr_rounding);
302 return result;
303 #else
304 unsigned int prec = mpfr_precision * 3;
305 MPFRNumber result(*this, prec);
306
307 float f = mpfr_get_flt(abs().value, mpfr_rounding);
308 if (f > 0.5f && f < 0x1.0p30f) {
309 mpfr_exp2(result.value, value, mpfr_rounding);
310 mpfr_sub_ui(result.value, result.value, 1, mpfr_rounding);
311 return result;
312 }
313
314 MPFRNumber ln2(2.0f, prec);
315 // log(2)
316 mpfr_log(ln2.value, ln2.value, mpfr_rounding);
317 // x * log(2)
318 mpfr_mul(result.value, value, ln2.value, mpfr_rounding);
319 // e^(x * log(2)) - 1
320 int ex = mpfr_expm1(result.value, result.value, mpfr_rounding);
321 mpfr_subnormalize(result.value, ex, mpfr_rounding);
322 return result;
323 #endif
324 }
325
exp10() const326 MPFRNumber exp10() const {
327 MPFRNumber result(*this);
328 mpfr_exp10(result.value, value, mpfr_rounding);
329 return result;
330 }
331
exp10m1() const332 MPFRNumber exp10m1() const {
333 // TODO: Only use mpfr_exp10m1 once CI and buildbots get MPFR >= 4.2.0.
334 #if MPFR_VERSION_MAJOR > 4 || \
335 (MPFR_VERSION_MAJOR == 4 && MPFR_VERSION_MINOR >= 2)
336 MPFRNumber result(*this);
337 mpfr_exp10m1(result.value, value, mpfr_rounding);
338 return result;
339 #else
340 unsigned int prec = mpfr_precision * 3;
341 MPFRNumber result(*this, prec);
342
343 MPFRNumber ln10(10.0f, prec);
344 // log(10)
345 mpfr_log(ln10.value, ln10.value, mpfr_rounding);
346 // x * log(10)
347 mpfr_mul(result.value, value, ln10.value, mpfr_rounding);
348 // e^(x * log(10)) - 1
349 int ex = mpfr_expm1(result.value, result.value, mpfr_rounding);
350 mpfr_subnormalize(result.value, ex, mpfr_rounding);
351 return result;
352 #endif
353 }
354
expm1() const355 MPFRNumber expm1() const {
356 MPFRNumber result(*this);
357 mpfr_expm1(result.value, value, mpfr_rounding);
358 return result;
359 }
360
div(const MPFRNumber & b) const361 MPFRNumber div(const MPFRNumber &b) const {
362 MPFRNumber result(*this);
363 mpfr_div(result.value, value, b.value, mpfr_rounding);
364 return result;
365 }
366
floor() const367 MPFRNumber floor() const {
368 MPFRNumber result(*this);
369 mpfr_floor(result.value, value);
370 return result;
371 }
372
fmod(const MPFRNumber & b)373 MPFRNumber fmod(const MPFRNumber &b) {
374 MPFRNumber result(*this);
375 mpfr_fmod(result.value, value, b.value, mpfr_rounding);
376 return result;
377 }
378
frexp(int & exp)379 MPFRNumber frexp(int &exp) {
380 MPFRNumber result(*this);
381 mpfr_exp_t resultExp;
382 mpfr_frexp(&resultExp, result.value, value, mpfr_rounding);
383 exp = resultExp;
384 return result;
385 }
386
hypot(const MPFRNumber & b)387 MPFRNumber hypot(const MPFRNumber &b) {
388 MPFRNumber result(*this);
389 mpfr_hypot(result.value, value, b.value, mpfr_rounding);
390 return result;
391 }
392
log() const393 MPFRNumber log() const {
394 MPFRNumber result(*this);
395 mpfr_log(result.value, value, mpfr_rounding);
396 return result;
397 }
398
log2() const399 MPFRNumber log2() const {
400 MPFRNumber result(*this);
401 mpfr_log2(result.value, value, mpfr_rounding);
402 return result;
403 }
404
log10() const405 MPFRNumber log10() const {
406 MPFRNumber result(*this);
407 mpfr_log10(result.value, value, mpfr_rounding);
408 return result;
409 }
410
log1p() const411 MPFRNumber log1p() const {
412 MPFRNumber result(*this);
413 mpfr_log1p(result.value, value, mpfr_rounding);
414 return result;
415 }
416
pow(const MPFRNumber & b)417 MPFRNumber pow(const MPFRNumber &b) {
418 MPFRNumber result(*this);
419 mpfr_pow(result.value, value, b.value, mpfr_rounding);
420 return result;
421 }
422
remquo(const MPFRNumber & divisor,int & quotient)423 MPFRNumber remquo(const MPFRNumber &divisor, int "ient) {
424 MPFRNumber remainder(*this);
425 long q;
426 mpfr_remquo(remainder.value, &q, value, divisor.value, mpfr_rounding);
427 quotient = q;
428 return remainder;
429 }
430
round() const431 MPFRNumber round() const {
432 MPFRNumber result(*this);
433 mpfr_round(result.value, value);
434 return result;
435 }
436
roundeven() const437 MPFRNumber roundeven() const {
438 MPFRNumber result(*this);
439 #if MPFR_VERSION_MAJOR >= 4
440 mpfr_roundeven(result.value, value);
441 #else
442 mpfr_rint(result.value, value, MPFR_RNDN);
443 #endif
444 return result;
445 }
446
round_to_long(long & result) const447 bool round_to_long(long &result) const {
448 // We first calculate the rounded value. This way, when converting
449 // to long using mpfr_get_si, the rounding direction of MPFR_RNDN
450 // (or any other rounding mode), does not have an influence.
451 MPFRNumber roundedValue = round();
452 mpfr_clear_erangeflag();
453 result = mpfr_get_si(roundedValue.value, MPFR_RNDN);
454 return mpfr_erangeflag_p();
455 }
456
round_to_long(mpfr_rnd_t rnd,long & result) const457 bool round_to_long(mpfr_rnd_t rnd, long &result) const {
458 MPFRNumber rint_result(*this);
459 mpfr_rint(rint_result.value, value, rnd);
460 return rint_result.round_to_long(result);
461 }
462
rint(mpfr_rnd_t rnd) const463 MPFRNumber rint(mpfr_rnd_t rnd) const {
464 MPFRNumber result(*this);
465 mpfr_rint(result.value, value, rnd);
466 return result;
467 }
468
mod_2pi() const469 MPFRNumber mod_2pi() const {
470 MPFRNumber result(0.0, 1280);
471 MPFRNumber _2pi(0.0, 1280);
472 mpfr_const_pi(_2pi.value, MPFR_RNDN);
473 mpfr_mul_si(_2pi.value, _2pi.value, 2, MPFR_RNDN);
474 mpfr_fmod(result.value, value, _2pi.value, MPFR_RNDN);
475 return result;
476 }
477
mod_pi_over_2() const478 MPFRNumber mod_pi_over_2() const {
479 MPFRNumber result(0.0, 1280);
480 MPFRNumber pi_over_2(0.0, 1280);
481 mpfr_const_pi(pi_over_2.value, MPFR_RNDN);
482 mpfr_mul_d(pi_over_2.value, pi_over_2.value, 0.5, MPFR_RNDN);
483 mpfr_fmod(result.value, value, pi_over_2.value, MPFR_RNDN);
484 return result;
485 }
486
mod_pi_over_4() const487 MPFRNumber mod_pi_over_4() const {
488 MPFRNumber result(0.0, 1280);
489 MPFRNumber pi_over_4(0.0, 1280);
490 mpfr_const_pi(pi_over_4.value, MPFR_RNDN);
491 mpfr_mul_d(pi_over_4.value, pi_over_4.value, 0.25, MPFR_RNDN);
492 mpfr_fmod(result.value, value, pi_over_4.value, MPFR_RNDN);
493 return result;
494 }
495
sin() const496 MPFRNumber sin() const {
497 MPFRNumber result(*this);
498 mpfr_sin(result.value, value, mpfr_rounding);
499 return result;
500 }
501
sinpi() const502 MPFRNumber sinpi() const {
503 MPFRNumber result(*this);
504
505 #if MPFR_VERSION_MAJOR > 4 || \
506 (MPFR_VERSION_MAJOR == 4 && MPFR_VERSION_MINOR >= 2)
507
508 mpfr_sinpi(result.value, value, mpfr_rounding);
509 return result;
510 #else
511 if (mpfr_integer_p(value)) {
512 mpfr_set_si(result.value, 0, mpfr_rounding);
513 return result;
514 }
515
516 MPFRNumber value_mul_two(*this);
517 mpfr_mul_si(value_mul_two.value, value, 2, MPFR_RNDN);
518
519 if (mpfr_integer_p(value_mul_two.value)) {
520 auto d = mpfr_get_si(value, MPFR_RNDD);
521 mpfr_set_si(result.value, (d & 1) ? -1 : 1, mpfr_rounding);
522 return result;
523 }
524
525 MPFRNumber value_pi(0.0, 1280);
526 mpfr_const_pi(value_pi.value, MPFR_RNDN);
527 mpfr_mul(value_pi.value, value_pi.value, value, MPFR_RNDN);
528 mpfr_sin(result.value, value_pi.value, mpfr_rounding);
529 return result;
530 #endif
531 }
532
sinh() const533 MPFRNumber sinh() const {
534 MPFRNumber result(*this);
535 mpfr_sinh(result.value, value, mpfr_rounding);
536 return result;
537 }
538
sqrt() const539 MPFRNumber sqrt() const {
540 MPFRNumber result(*this);
541 mpfr_sqrt(result.value, value, mpfr_rounding);
542 return result;
543 }
544
sub(const MPFRNumber & b) const545 MPFRNumber sub(const MPFRNumber &b) const {
546 MPFRNumber result(*this);
547 mpfr_sub(result.value, value, b.value, mpfr_rounding);
548 return result;
549 }
550
tan() const551 MPFRNumber tan() const {
552 MPFRNumber result(*this);
553 mpfr_tan(result.value, value, mpfr_rounding);
554 return result;
555 }
556
tanh() const557 MPFRNumber tanh() const {
558 MPFRNumber result(*this);
559 mpfr_tanh(result.value, value, mpfr_rounding);
560 return result;
561 }
562
tanpi() const563 MPFRNumber tanpi() const {
564 MPFRNumber result(*this);
565
566 #if MPFR_VERSION_MAJOR > 4 || \
567 (MPFR_VERSION_MAJOR == 4 && MPFR_VERSION_MINOR >= 2)
568
569 mpfr_tanpi(result.value, value, mpfr_rounding);
570 return result;
571 #else
572 MPFRNumber value_ret_exact(*this);
573 MPFRNumber value_one(*this);
574 mpfr_set_si(value_one.value, 1, MPFR_RNDN);
575 mpfr_fmod(value_ret_exact.value, value, value_one.value, mpfr_rounding);
576 mpfr_mul_si(value_ret_exact.value, value_ret_exact.value, 4, MPFR_RNDN);
577
578 if (mpfr_integer_p(value_ret_exact.value)) {
579 int mod = mpfr_get_si(value_ret_exact.value, MPFR_RNDN);
580 mod = (mod < 0 ? -1 * mod : mod);
581
582 switch (mod) {
583 case 0:
584 mpfr_set_si(result.value, 0, mpfr_rounding);
585 break;
586 case 1:
587 mpfr_set_si(result.value, (mpfr_signbit(value) ? -1 : 1),
588 mpfr_rounding);
589 break;
590 case 2: {
591 auto d = mpfr_get_si(value, MPFR_RNDZ);
592 d += mpfr_sgn(value) > 0 ? 0 : 1;
593 mpfr_set_inf(result.value, (d & 1) ? -1 : 1);
594 break;
595 }
596 case 3:
597 mpfr_set_si(result.value, (mpfr_signbit(value) ? 1 : -1),
598 mpfr_rounding);
599 break;
600 }
601
602 return result;
603 }
604
605 MPFRNumber value_pi(0.0, 1280);
606 mpfr_const_pi(value_pi.value, MPFR_RNDN);
607 mpfr_mul(value_pi.value, value_pi.value, value, MPFR_RNDN);
608 mpfr_tan(result.value, value_pi.value, mpfr_rounding);
609 return result;
610 #endif
611 }
612
trunc() const613 MPFRNumber trunc() const {
614 MPFRNumber result(*this);
615 mpfr_trunc(result.value, value);
616 return result;
617 }
618
fma(const MPFRNumber & b,const MPFRNumber & c)619 MPFRNumber fma(const MPFRNumber &b, const MPFRNumber &c) {
620 MPFRNumber result(*this);
621 mpfr_fma(result.value, value, b.value, c.value, mpfr_rounding);
622 return result;
623 }
624
mul(const MPFRNumber & b)625 MPFRNumber mul(const MPFRNumber &b) {
626 MPFRNumber result(*this);
627 mpfr_mul(result.value, value, b.value, mpfr_rounding);
628 return result;
629 }
630
str() const631 cpp::string str() const {
632 // 200 bytes should be more than sufficient to hold a 100-digit number
633 // plus additional bytes for the decimal point, '-' sign etc.
634 constexpr size_t printBufSize = 200;
635 char buffer[printBufSize];
636 mpfr_snprintf(buffer, printBufSize, "%100.50Rf", value);
637 cpp::string_view view(buffer);
638 // Trim whitespaces
639 const char whitespace = ' ';
640 while (!view.empty() && view.front() == whitespace)
641 view.remove_prefix(1);
642 while (!view.empty() && view.back() == whitespace)
643 view.remove_suffix(1);
644 return cpp::string(view.data());
645 }
646
647 // These functions are useful for debugging.
648 template <typename T> T as() const;
649
dump(const char * msg) const650 void dump(const char *msg) const { mpfr_printf("%s%.128Rf\n", msg, value); }
651
652 // Return the ULP (units-in-the-last-place) difference between the
653 // stored MPFR and a floating point number.
654 //
655 // We define ULP difference as follows:
656 // If exponents of this value and the |input| are same, then:
657 // ULP(this_value, input) = abs(this_value - input) / eps(input)
658 // else:
659 // max = max(abs(this_value), abs(input))
660 // min = min(abs(this_value), abs(input))
661 // maxExponent = exponent(max)
662 // ULP(this_value, input) = (max - 2^maxExponent) / eps(max) +
663 // (2^maxExponent - min) / eps(min)
664 //
665 // Remarks:
666 // 1. A ULP of 0.0 will imply that the value is correctly rounded.
667 // 2. We expect that this value and the value to be compared (the [input]
668 // argument) are reasonable close, and we will provide an upper bound
669 // of ULP value for testing. Morever, most of the fractional parts of
670 // ULP value do not matter much, so using double as the return type
671 // should be good enough.
672 // 3. For close enough values (values which don't diff in their exponent by
673 // not more than 1), a ULP difference of N indicates a bit distance
674 // of N between this number and [input].
675 // 4. A values of +0.0 and -0.0 are treated as equal.
676 template <typename T>
677 cpp::enable_if_t<cpp::is_floating_point_v<T>, MPFRNumber>
ulp_as_mpfr_number(T input)678 ulp_as_mpfr_number(T input) {
679 T thisAsT = as<T>();
680 if (thisAsT == input)
681 return MPFRNumber(0.0);
682
683 if (is_nan()) {
684 if (FPBits<T>(input).is_nan())
685 return MPFRNumber(0.0);
686 return MPFRNumber(FPBits<T>::inf().get_val());
687 }
688
689 int thisExponent = FPBits<T>(thisAsT).get_exponent();
690 int inputExponent = FPBits<T>(input).get_exponent();
691 // Adjust the exponents for denormal numbers.
692 if (FPBits<T>(thisAsT).is_subnormal())
693 ++thisExponent;
694 if (FPBits<T>(input).is_subnormal())
695 ++inputExponent;
696
697 if (thisAsT * input < 0 || thisExponent == inputExponent) {
698 MPFRNumber inputMPFR(input);
699 mpfr_sub(inputMPFR.value, value, inputMPFR.value, MPFR_RNDN);
700 mpfr_abs(inputMPFR.value, inputMPFR.value, MPFR_RNDN);
701 mpfr_mul_2si(inputMPFR.value, inputMPFR.value,
702 -thisExponent + FPBits<T>::FRACTION_LEN, MPFR_RNDN);
703 return inputMPFR;
704 }
705
706 // If the control reaches here, it means that this number and input are
707 // of the same sign but different exponent. In such a case, ULP error is
708 // calculated as sum of two parts.
709 thisAsT = FPBits<T>(thisAsT).abs().get_val();
710 input = FPBits<T>(input).abs().get_val();
711 T min = thisAsT > input ? input : thisAsT;
712 T max = thisAsT > input ? thisAsT : input;
713 int minExponent = FPBits<T>(min).get_exponent();
714 int maxExponent = FPBits<T>(max).get_exponent();
715 // Adjust the exponents for denormal numbers.
716 if (FPBits<T>(min).is_subnormal())
717 ++minExponent;
718 if (FPBits<T>(max).is_subnormal())
719 ++maxExponent;
720
721 MPFRNumber minMPFR(min);
722 MPFRNumber maxMPFR(max);
723
724 MPFRNumber pivot(uint32_t(1));
725 mpfr_mul_2si(pivot.value, pivot.value, maxExponent, MPFR_RNDN);
726
727 mpfr_sub(minMPFR.value, pivot.value, minMPFR.value, MPFR_RNDN);
728 mpfr_mul_2si(minMPFR.value, minMPFR.value,
729 -minExponent + FPBits<T>::FRACTION_LEN, MPFR_RNDN);
730
731 mpfr_sub(maxMPFR.value, maxMPFR.value, pivot.value, MPFR_RNDN);
732 mpfr_mul_2si(maxMPFR.value, maxMPFR.value,
733 -maxExponent + FPBits<T>::FRACTION_LEN, MPFR_RNDN);
734
735 mpfr_add(minMPFR.value, minMPFR.value, maxMPFR.value, MPFR_RNDN);
736 return minMPFR;
737 }
738
739 template <typename T>
740 cpp::enable_if_t<cpp::is_floating_point_v<T>, cpp::string>
ulp_as_string(T input)741 ulp_as_string(T input) {
742 MPFRNumber num = ulp_as_mpfr_number(input);
743 return num.str();
744 }
745
746 template <typename T>
ulp(T input)747 cpp::enable_if_t<cpp::is_floating_point_v<T>, double> ulp(T input) {
748 MPFRNumber num = ulp_as_mpfr_number(input);
749 return num.as<double>();
750 }
751 };
752
as() const753 template <> float MPFRNumber::as<float>() const {
754 return mpfr_get_flt(value, mpfr_rounding);
755 }
756
as() const757 template <> double MPFRNumber::as<double>() const {
758 return mpfr_get_d(value, mpfr_rounding);
759 }
760
as() const761 template <> long double MPFRNumber::as<long double>() const {
762 return mpfr_get_ld(value, mpfr_rounding);
763 }
764
765 #ifdef LIBC_TYPES_HAS_FLOAT16
as() const766 template <> float16 MPFRNumber::as<float16>() const {
767 // TODO: Either prove that this cast won't cause double-rounding errors, or
768 // find a better way to get a float16.
769 return fputil::cast<float16>(mpfr_get_d(value, mpfr_rounding));
770 }
771 #endif
772
773 namespace internal {
774
775 template <typename InputType>
776 cpp::enable_if_t<cpp::is_floating_point_v<InputType>, MPFRNumber>
unary_operation(Operation op,InputType input,unsigned int precision,RoundingMode rounding)777 unary_operation(Operation op, InputType input, unsigned int precision,
778 RoundingMode rounding) {
779 MPFRNumber mpfrInput(input, precision, rounding);
780 switch (op) {
781 case Operation::Abs:
782 return mpfrInput.abs();
783 case Operation::Acos:
784 return mpfrInput.acos();
785 case Operation::Acosh:
786 return mpfrInput.acosh();
787 case Operation::Asin:
788 return mpfrInput.asin();
789 case Operation::Asinh:
790 return mpfrInput.asinh();
791 case Operation::Atan:
792 return mpfrInput.atan();
793 case Operation::Atanh:
794 return mpfrInput.atanh();
795 case Operation::Cbrt:
796 return mpfrInput.cbrt();
797 case Operation::Ceil:
798 return mpfrInput.ceil();
799 case Operation::Cos:
800 return mpfrInput.cos();
801 case Operation::Cosh:
802 return mpfrInput.cosh();
803 case Operation::Cospi:
804 return mpfrInput.cospi();
805 case Operation::Erf:
806 return mpfrInput.erf();
807 case Operation::Exp:
808 return mpfrInput.exp();
809 case Operation::Exp2:
810 return mpfrInput.exp2();
811 case Operation::Exp2m1:
812 return mpfrInput.exp2m1();
813 case Operation::Exp10:
814 return mpfrInput.exp10();
815 case Operation::Exp10m1:
816 return mpfrInput.exp10m1();
817 case Operation::Expm1:
818 return mpfrInput.expm1();
819 case Operation::Floor:
820 return mpfrInput.floor();
821 case Operation::Log:
822 return mpfrInput.log();
823 case Operation::Log2:
824 return mpfrInput.log2();
825 case Operation::Log10:
826 return mpfrInput.log10();
827 case Operation::Log1p:
828 return mpfrInput.log1p();
829 case Operation::Mod2PI:
830 return mpfrInput.mod_2pi();
831 case Operation::ModPIOver2:
832 return mpfrInput.mod_pi_over_2();
833 case Operation::ModPIOver4:
834 return mpfrInput.mod_pi_over_4();
835 case Operation::Round:
836 return mpfrInput.round();
837 case Operation::RoundEven:
838 return mpfrInput.roundeven();
839 case Operation::Sin:
840 return mpfrInput.sin();
841 case Operation::Sinpi:
842 return mpfrInput.sinpi();
843 case Operation::Sinh:
844 return mpfrInput.sinh();
845 case Operation::Sqrt:
846 return mpfrInput.sqrt();
847 case Operation::Tan:
848 return mpfrInput.tan();
849 case Operation::Tanh:
850 return mpfrInput.tanh();
851 case Operation::Tanpi:
852 return mpfrInput.tanpi();
853 case Operation::Trunc:
854 return mpfrInput.trunc();
855 default:
856 __builtin_unreachable();
857 }
858 }
859
860 template <typename InputType>
861 cpp::enable_if_t<cpp::is_floating_point_v<InputType>, MPFRNumber>
unary_operation_two_outputs(Operation op,InputType input,int & output,unsigned int precision,RoundingMode rounding)862 unary_operation_two_outputs(Operation op, InputType input, int &output,
863 unsigned int precision, RoundingMode rounding) {
864 MPFRNumber mpfrInput(input, precision, rounding);
865 switch (op) {
866 case Operation::Frexp:
867 return mpfrInput.frexp(output);
868 default:
869 __builtin_unreachable();
870 }
871 }
872
873 template <typename InputType>
874 cpp::enable_if_t<cpp::is_floating_point_v<InputType>, MPFRNumber>
binary_operation_one_output(Operation op,InputType x,InputType y,unsigned int precision,RoundingMode rounding)875 binary_operation_one_output(Operation op, InputType x, InputType y,
876 unsigned int precision, RoundingMode rounding) {
877 MPFRNumber inputX(x, precision, rounding);
878 MPFRNumber inputY(y, precision, rounding);
879 switch (op) {
880 case Operation::Add:
881 return inputX.add(inputY);
882 case Operation::Atan2:
883 return inputX.atan2(inputY);
884 case Operation::Div:
885 return inputX.div(inputY);
886 case Operation::Fmod:
887 return inputX.fmod(inputY);
888 case Operation::Hypot:
889 return inputX.hypot(inputY);
890 case Operation::Mul:
891 return inputX.mul(inputY);
892 case Operation::Pow:
893 return inputX.pow(inputY);
894 case Operation::Sub:
895 return inputX.sub(inputY);
896 default:
897 __builtin_unreachable();
898 }
899 }
900
901 template <typename InputType>
902 cpp::enable_if_t<cpp::is_floating_point_v<InputType>, MPFRNumber>
binary_operation_two_outputs(Operation op,InputType x,InputType y,int & output,unsigned int precision,RoundingMode rounding)903 binary_operation_two_outputs(Operation op, InputType x, InputType y,
904 int &output, unsigned int precision,
905 RoundingMode rounding) {
906 MPFRNumber inputX(x, precision, rounding);
907 MPFRNumber inputY(y, precision, rounding);
908 switch (op) {
909 case Operation::RemQuo:
910 return inputX.remquo(inputY, output);
911 default:
912 __builtin_unreachable();
913 }
914 }
915
916 template <typename InputType>
917 cpp::enable_if_t<cpp::is_floating_point_v<InputType>, MPFRNumber>
ternary_operation_one_output(Operation op,InputType x,InputType y,InputType z,unsigned int precision,RoundingMode rounding)918 ternary_operation_one_output(Operation op, InputType x, InputType y,
919 InputType z, unsigned int precision,
920 RoundingMode rounding) {
921 // For FMA function, we just need to compare with the mpfr_fma with the same
922 // precision as InputType. Using higher precision as the intermediate results
923 // to compare might incorrectly fail due to double-rounding errors.
924 MPFRNumber inputX(x, precision, rounding);
925 MPFRNumber inputY(y, precision, rounding);
926 MPFRNumber inputZ(z, precision, rounding);
927 switch (op) {
928 case Operation::Fma:
929 return inputX.fma(inputY, inputZ);
930 default:
931 __builtin_unreachable();
932 }
933 }
934
935 // Remark: For all the explain_*_error functions, we will use std::stringstream
936 // to build the complete error messages before sending it to the outstream `OS`
937 // once at the end. This will stop the error messages from interleaving when
938 // the tests are running concurrently.
939 template <typename InputType, typename OutputType>
explain_unary_operation_single_output_error(Operation op,InputType input,OutputType matchValue,double ulp_tolerance,RoundingMode rounding)940 void explain_unary_operation_single_output_error(Operation op, InputType input,
941 OutputType matchValue,
942 double ulp_tolerance,
943 RoundingMode rounding) {
944 unsigned int precision = get_precision<InputType>(ulp_tolerance);
945 MPFRNumber mpfrInput(input, precision);
946 MPFRNumber mpfr_result;
947 mpfr_result = unary_operation(op, input, precision, rounding);
948 MPFRNumber mpfrMatchValue(matchValue);
949 cpp::array<char, 1024> msg_buf;
950 cpp::StringStream msg(msg_buf);
951 msg << "Match value not within tolerance value of MPFR result:\n"
952 << " Input decimal: " << mpfrInput.str() << '\n';
953 msg << " Input bits: " << str(FPBits<InputType>(input)) << '\n';
954 msg << '\n' << " Match decimal: " << mpfrMatchValue.str() << '\n';
955 msg << " Match bits: " << str(FPBits<OutputType>(matchValue)) << '\n';
956 msg << '\n' << " MPFR result: " << mpfr_result.str() << '\n';
957 msg << " MPFR rounded: "
958 << str(FPBits<OutputType>(mpfr_result.as<OutputType>())) << '\n';
959 msg << '\n';
960 msg << " ULP error: " << mpfr_result.ulp_as_mpfr_number(matchValue).str()
961 << '\n';
962 if (msg.overflow())
963 __builtin_unreachable();
964 tlog << msg.str();
965 }
966
967 template void explain_unary_operation_single_output_error(Operation op, float,
968 float, double,
969 RoundingMode);
970 template void explain_unary_operation_single_output_error(Operation op, double,
971 double, double,
972 RoundingMode);
973 template void explain_unary_operation_single_output_error(Operation op,
974 long double,
975 long double, double,
976 RoundingMode);
977 template void explain_unary_operation_single_output_error(Operation op, double,
978 float, double,
979 RoundingMode);
980 template void explain_unary_operation_single_output_error(Operation op,
981 long double, float,
982 double, RoundingMode);
983 template void explain_unary_operation_single_output_error(Operation op,
984 long double, double,
985 double, RoundingMode);
986
987 #ifdef LIBC_TYPES_HAS_FLOAT16
988 template void explain_unary_operation_single_output_error(Operation op, float16,
989 float16, double,
990 RoundingMode);
991 template void explain_unary_operation_single_output_error(Operation op, float,
992 float16, double,
993 RoundingMode);
994 template void explain_unary_operation_single_output_error(Operation op, double,
995 float16, double,
996 RoundingMode);
997 template void explain_unary_operation_single_output_error(Operation op,
998 long double, float16,
999 double, RoundingMode);
1000 #endif
1001
1002 template <typename T>
explain_unary_operation_two_outputs_error(Operation op,T input,const BinaryOutput<T> & libc_result,double ulp_tolerance,RoundingMode rounding)1003 void explain_unary_operation_two_outputs_error(
1004 Operation op, T input, const BinaryOutput<T> &libc_result,
1005 double ulp_tolerance, RoundingMode rounding) {
1006 unsigned int precision = get_precision<T>(ulp_tolerance);
1007 MPFRNumber mpfrInput(input, precision);
1008 int mpfrIntResult;
1009 MPFRNumber mpfr_result = unary_operation_two_outputs(op, input, mpfrIntResult,
1010 precision, rounding);
1011
1012 if (mpfrIntResult != libc_result.i) {
1013 tlog << "MPFR integral result: " << mpfrIntResult << '\n'
1014 << "Libc integral result: " << libc_result.i << '\n';
1015 } else {
1016 tlog << "Integral result from libc matches integral result from MPFR.\n";
1017 }
1018
1019 MPFRNumber mpfrMatchValue(libc_result.f);
1020 tlog
1021 << "Libc floating point result is not within tolerance value of the MPFR "
1022 << "result.\n\n";
1023
1024 tlog << " Input decimal: " << mpfrInput.str() << "\n\n";
1025
1026 tlog << "Libc floating point value: " << mpfrMatchValue.str() << '\n';
1027 tlog << " Libc floating point bits: " << str(FPBits<T>(libc_result.f))
1028 << '\n';
1029 tlog << "\n\n";
1030
1031 tlog << " MPFR result: " << mpfr_result.str() << '\n';
1032 tlog << " MPFR rounded: " << str(FPBits<T>(mpfr_result.as<T>()))
1033 << '\n';
1034 tlog << '\n'
1035 << " ULP error: "
1036 << mpfr_result.ulp_as_mpfr_number(libc_result.f).str() << '\n';
1037 }
1038
1039 template void explain_unary_operation_two_outputs_error<float>(
1040 Operation, float, const BinaryOutput<float> &, double, RoundingMode);
1041 template void explain_unary_operation_two_outputs_error<double>(
1042 Operation, double, const BinaryOutput<double> &, double, RoundingMode);
1043 template void explain_unary_operation_two_outputs_error<long double>(
1044 Operation, long double, const BinaryOutput<long double> &, double,
1045 RoundingMode);
1046
1047 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)1048 void explain_binary_operation_two_outputs_error(
1049 Operation op, const BinaryInput<T> &input,
1050 const BinaryOutput<T> &libc_result, double ulp_tolerance,
1051 RoundingMode rounding) {
1052 unsigned int precision = get_precision<T>(ulp_tolerance);
1053 MPFRNumber mpfrX(input.x, precision);
1054 MPFRNumber mpfrY(input.y, precision);
1055 int mpfrIntResult;
1056 MPFRNumber mpfr_result = binary_operation_two_outputs(
1057 op, input.x, input.y, mpfrIntResult, precision, rounding);
1058 MPFRNumber mpfrMatchValue(libc_result.f);
1059
1060 tlog << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n'
1061 << "MPFR integral result: " << mpfrIntResult << '\n'
1062 << "Libc integral result: " << libc_result.i << '\n'
1063 << "Libc floating point result: " << mpfrMatchValue.str() << '\n'
1064 << " MPFR result: " << mpfr_result.str() << '\n';
1065 tlog << "Libc floating point result bits: " << str(FPBits<T>(libc_result.f))
1066 << '\n';
1067 tlog << " MPFR rounded bits: "
1068 << str(FPBits<T>(mpfr_result.as<T>())) << '\n';
1069 tlog << "ULP error: " << mpfr_result.ulp_as_mpfr_number(libc_result.f).str()
1070 << '\n';
1071 }
1072
1073 template void explain_binary_operation_two_outputs_error<float>(
1074 Operation, const BinaryInput<float> &, const BinaryOutput<float> &, double,
1075 RoundingMode);
1076 template void explain_binary_operation_two_outputs_error<double>(
1077 Operation, const BinaryInput<double> &, const BinaryOutput<double> &,
1078 double, RoundingMode);
1079 template void explain_binary_operation_two_outputs_error<long double>(
1080 Operation, const BinaryInput<long double> &,
1081 const BinaryOutput<long double> &, double, RoundingMode);
1082
1083 template <typename InputType, typename OutputType>
explain_binary_operation_one_output_error(Operation op,const BinaryInput<InputType> & input,OutputType libc_result,double ulp_tolerance,RoundingMode rounding)1084 void explain_binary_operation_one_output_error(
1085 Operation op, const BinaryInput<InputType> &input, OutputType libc_result,
1086 double ulp_tolerance, RoundingMode rounding) {
1087 unsigned int precision = get_precision<InputType>(ulp_tolerance);
1088 MPFRNumber mpfrX(input.x, precision);
1089 MPFRNumber mpfrY(input.y, precision);
1090 FPBits<InputType> xbits(input.x);
1091 FPBits<InputType> ybits(input.y);
1092 MPFRNumber mpfr_result =
1093 binary_operation_one_output(op, input.x, input.y, precision, rounding);
1094 MPFRNumber mpfrMatchValue(libc_result);
1095
1096 tlog << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n';
1097 tlog << "First input bits: " << str(FPBits<InputType>(input.x)) << '\n';
1098 tlog << "Second input bits: " << str(FPBits<InputType>(input.y)) << '\n';
1099
1100 tlog << "Libc result: " << mpfrMatchValue.str() << '\n'
1101 << "MPFR result: " << mpfr_result.str() << '\n';
1102 tlog << "Libc floating point result bits: "
1103 << str(FPBits<OutputType>(libc_result)) << '\n';
1104 tlog << " MPFR rounded bits: "
1105 << str(FPBits<OutputType>(mpfr_result.as<OutputType>())) << '\n';
1106 tlog << "ULP error: " << mpfr_result.ulp_as_mpfr_number(libc_result).str()
1107 << '\n';
1108 }
1109
1110 template void
1111 explain_binary_operation_one_output_error(Operation, const BinaryInput<float> &,
1112 float, double, RoundingMode);
1113 template void explain_binary_operation_one_output_error(
1114 Operation, const BinaryInput<double> &, float, double, RoundingMode);
1115 template void explain_binary_operation_one_output_error(
1116 Operation, const BinaryInput<double> &, double, double, RoundingMode);
1117 template void explain_binary_operation_one_output_error(
1118 Operation, const BinaryInput<long double> &, float, double, RoundingMode);
1119 template void explain_binary_operation_one_output_error(
1120 Operation, const BinaryInput<long double> &, double, double, RoundingMode);
1121 template void
1122 explain_binary_operation_one_output_error(Operation,
1123 const BinaryInput<long double> &,
1124 long double, double, RoundingMode);
1125 #ifdef LIBC_TYPES_HAS_FLOAT16
1126 template void explain_binary_operation_one_output_error(
1127 Operation, const BinaryInput<float16> &, float16, double, RoundingMode);
1128 template void
1129 explain_binary_operation_one_output_error(Operation, const BinaryInput<float> &,
1130 float16, double, RoundingMode);
1131 template void explain_binary_operation_one_output_error(
1132 Operation, const BinaryInput<double> &, float16, double, RoundingMode);
1133 template void explain_binary_operation_one_output_error(
1134 Operation, const BinaryInput<long double> &, float16, double, RoundingMode);
1135 #endif
1136
1137 template <typename InputType, typename OutputType>
explain_ternary_operation_one_output_error(Operation op,const TernaryInput<InputType> & input,OutputType libc_result,double ulp_tolerance,RoundingMode rounding)1138 void explain_ternary_operation_one_output_error(
1139 Operation op, const TernaryInput<InputType> &input, OutputType libc_result,
1140 double ulp_tolerance, RoundingMode rounding) {
1141 unsigned int precision = get_precision<InputType>(ulp_tolerance);
1142 MPFRNumber mpfrX(input.x, precision);
1143 MPFRNumber mpfrY(input.y, precision);
1144 MPFRNumber mpfrZ(input.z, precision);
1145 FPBits<InputType> xbits(input.x);
1146 FPBits<InputType> ybits(input.y);
1147 FPBits<InputType> zbits(input.z);
1148 MPFRNumber mpfr_result = ternary_operation_one_output(
1149 op, input.x, input.y, input.z, precision, rounding);
1150 MPFRNumber mpfrMatchValue(libc_result);
1151
1152 tlog << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str()
1153 << " z: " << mpfrZ.str() << '\n';
1154 tlog << " First input bits: " << str(FPBits<InputType>(input.x)) << '\n';
1155 tlog << "Second input bits: " << str(FPBits<InputType>(input.y)) << '\n';
1156 tlog << " Third input bits: " << str(FPBits<InputType>(input.z)) << '\n';
1157
1158 tlog << "Libc result: " << mpfrMatchValue.str() << '\n'
1159 << "MPFR result: " << mpfr_result.str() << '\n';
1160 tlog << "Libc floating point result bits: "
1161 << str(FPBits<OutputType>(libc_result)) << '\n';
1162 tlog << " MPFR rounded bits: "
1163 << str(FPBits<OutputType>(mpfr_result.as<OutputType>())) << '\n';
1164 tlog << "ULP error: " << mpfr_result.ulp_as_mpfr_number(libc_result).str()
1165 << '\n';
1166 }
1167
1168 template void explain_ternary_operation_one_output_error(
1169 Operation, const TernaryInput<float> &, float, double, RoundingMode);
1170 template void explain_ternary_operation_one_output_error(
1171 Operation, const TernaryInput<double> &, float, double, RoundingMode);
1172 template void explain_ternary_operation_one_output_error(
1173 Operation, const TernaryInput<double> &, double, double, RoundingMode);
1174 template void explain_ternary_operation_one_output_error(
1175 Operation, const TernaryInput<long double> &, float, double, RoundingMode);
1176 template void explain_ternary_operation_one_output_error(
1177 Operation, const TernaryInput<long double> &, double, double, RoundingMode);
1178 template void
1179 explain_ternary_operation_one_output_error(Operation,
1180 const TernaryInput<long double> &,
1181 long double, double, RoundingMode);
1182
1183 #ifdef LIBC_TYPES_HAS_FLOAT16
1184 template void explain_ternary_operation_one_output_error(
1185 Operation, const TernaryInput<float> &, float16, double, RoundingMode);
1186 template void explain_ternary_operation_one_output_error(
1187 Operation, const TernaryInput<double> &, float16, double, RoundingMode);
1188 template void
1189 explain_ternary_operation_one_output_error(Operation,
1190 const TernaryInput<long double> &,
1191 float16, double, RoundingMode);
1192 #endif
1193
1194 template <typename InputType, typename OutputType>
compare_unary_operation_single_output(Operation op,InputType input,OutputType libc_result,double ulp_tolerance,RoundingMode rounding)1195 bool compare_unary_operation_single_output(Operation op, InputType input,
1196 OutputType libc_result,
1197 double ulp_tolerance,
1198 RoundingMode rounding) {
1199 unsigned int precision = get_precision<InputType>(ulp_tolerance);
1200 MPFRNumber mpfr_result;
1201 mpfr_result = unary_operation(op, input, precision, rounding);
1202 double ulp = mpfr_result.ulp(libc_result);
1203 return (ulp <= ulp_tolerance);
1204 }
1205
1206 template bool compare_unary_operation_single_output(Operation, float, float,
1207 double, RoundingMode);
1208 template bool compare_unary_operation_single_output(Operation, double, double,
1209 double, RoundingMode);
1210 template bool compare_unary_operation_single_output(Operation, long double,
1211 long double, double,
1212 RoundingMode);
1213 template bool compare_unary_operation_single_output(Operation, double, float,
1214 double, RoundingMode);
1215 template bool compare_unary_operation_single_output(Operation, long double,
1216 float, double,
1217 RoundingMode);
1218 template bool compare_unary_operation_single_output(Operation, long double,
1219 double, double,
1220 RoundingMode);
1221 #ifdef LIBC_TYPES_HAS_FLOAT16
1222 template bool compare_unary_operation_single_output(Operation, float16, float16,
1223 double, RoundingMode);
1224 template bool compare_unary_operation_single_output(Operation, float, float16,
1225 double, RoundingMode);
1226 template bool compare_unary_operation_single_output(Operation, double, float16,
1227 double, RoundingMode);
1228 template bool compare_unary_operation_single_output(Operation, long double,
1229 float16, double,
1230 RoundingMode);
1231 #endif
1232
1233 template <typename T>
compare_unary_operation_two_outputs(Operation op,T input,const BinaryOutput<T> & libc_result,double ulp_tolerance,RoundingMode rounding)1234 bool compare_unary_operation_two_outputs(Operation op, T input,
1235 const BinaryOutput<T> &libc_result,
1236 double ulp_tolerance,
1237 RoundingMode rounding) {
1238 int mpfrIntResult;
1239 unsigned int precision = get_precision<T>(ulp_tolerance);
1240 MPFRNumber mpfr_result = unary_operation_two_outputs(op, input, mpfrIntResult,
1241 precision, rounding);
1242 double ulp = mpfr_result.ulp(libc_result.f);
1243
1244 if (mpfrIntResult != libc_result.i)
1245 return false;
1246
1247 return (ulp <= ulp_tolerance);
1248 }
1249
1250 template bool compare_unary_operation_two_outputs<float>(
1251 Operation, float, const BinaryOutput<float> &, double, RoundingMode);
1252 template bool compare_unary_operation_two_outputs<double>(
1253 Operation, double, const BinaryOutput<double> &, double, RoundingMode);
1254 template bool compare_unary_operation_two_outputs<long double>(
1255 Operation, long double, const BinaryOutput<long double> &, double,
1256 RoundingMode);
1257
1258 template <typename T>
compare_binary_operation_two_outputs(Operation op,const BinaryInput<T> & input,const BinaryOutput<T> & libc_result,double ulp_tolerance,RoundingMode rounding)1259 bool compare_binary_operation_two_outputs(Operation op,
1260 const BinaryInput<T> &input,
1261 const BinaryOutput<T> &libc_result,
1262 double ulp_tolerance,
1263 RoundingMode rounding) {
1264 int mpfrIntResult;
1265 unsigned int precision = get_precision<T>(ulp_tolerance);
1266 MPFRNumber mpfr_result = binary_operation_two_outputs(
1267 op, input.x, input.y, mpfrIntResult, precision, rounding);
1268 double ulp = mpfr_result.ulp(libc_result.f);
1269
1270 if (mpfrIntResult != libc_result.i) {
1271 if (op == Operation::RemQuo) {
1272 if ((0x7 & mpfrIntResult) != (0x7 & libc_result.i))
1273 return false;
1274 } else {
1275 return false;
1276 }
1277 }
1278
1279 return (ulp <= ulp_tolerance);
1280 }
1281
1282 template bool compare_binary_operation_two_outputs<float>(
1283 Operation, const BinaryInput<float> &, const BinaryOutput<float> &, double,
1284 RoundingMode);
1285 template bool compare_binary_operation_two_outputs<double>(
1286 Operation, const BinaryInput<double> &, const BinaryOutput<double> &,
1287 double, RoundingMode);
1288 template bool compare_binary_operation_two_outputs<long double>(
1289 Operation, const BinaryInput<long double> &,
1290 const BinaryOutput<long double> &, double, RoundingMode);
1291
1292 template <typename InputType, typename OutputType>
compare_binary_operation_one_output(Operation op,const BinaryInput<InputType> & input,OutputType libc_result,double ulp_tolerance,RoundingMode rounding)1293 bool compare_binary_operation_one_output(Operation op,
1294 const BinaryInput<InputType> &input,
1295 OutputType libc_result,
1296 double ulp_tolerance,
1297 RoundingMode rounding) {
1298 unsigned int precision = get_precision<InputType>(ulp_tolerance);
1299 MPFRNumber mpfr_result =
1300 binary_operation_one_output(op, input.x, input.y, precision, rounding);
1301 double ulp = mpfr_result.ulp(libc_result);
1302
1303 return (ulp <= ulp_tolerance);
1304 }
1305
1306 template bool compare_binary_operation_one_output(Operation,
1307 const BinaryInput<float> &,
1308 float, double, RoundingMode);
1309 template bool compare_binary_operation_one_output(Operation,
1310 const BinaryInput<double> &,
1311 double, double, RoundingMode);
1312 template bool
1313 compare_binary_operation_one_output(Operation, const BinaryInput<long double> &,
1314 float, double, RoundingMode);
1315 template bool
1316 compare_binary_operation_one_output(Operation, const BinaryInput<long double> &,
1317 double, double, RoundingMode);
1318 template bool
1319 compare_binary_operation_one_output(Operation, const BinaryInput<long double> &,
1320 long double, double, RoundingMode);
1321
1322 template bool compare_binary_operation_one_output(Operation,
1323 const BinaryInput<double> &,
1324 float, double, RoundingMode);
1325 #ifdef LIBC_TYPES_HAS_FLOAT16
1326 template bool compare_binary_operation_one_output(Operation,
1327 const BinaryInput<float16> &,
1328 float16, double,
1329 RoundingMode);
1330 template bool compare_binary_operation_one_output(Operation,
1331 const BinaryInput<float> &,
1332 float16, double,
1333 RoundingMode);
1334 template bool compare_binary_operation_one_output(Operation,
1335 const BinaryInput<double> &,
1336 float16, double,
1337 RoundingMode);
1338 template bool
1339 compare_binary_operation_one_output(Operation, const BinaryInput<long double> &,
1340 float16, double, RoundingMode);
1341 #endif
1342
1343 template <typename InputType, typename OutputType>
compare_ternary_operation_one_output(Operation op,const TernaryInput<InputType> & input,OutputType libc_result,double ulp_tolerance,RoundingMode rounding)1344 bool compare_ternary_operation_one_output(Operation op,
1345 const TernaryInput<InputType> &input,
1346 OutputType libc_result,
1347 double ulp_tolerance,
1348 RoundingMode rounding) {
1349 unsigned int precision = get_precision<InputType>(ulp_tolerance);
1350 MPFRNumber mpfr_result = ternary_operation_one_output(
1351 op, input.x, input.y, input.z, precision, rounding);
1352 double ulp = mpfr_result.ulp(libc_result);
1353
1354 return (ulp <= ulp_tolerance);
1355 }
1356
1357 template bool compare_ternary_operation_one_output(Operation,
1358 const TernaryInput<float> &,
1359 float, double, RoundingMode);
1360 template bool compare_ternary_operation_one_output(Operation,
1361 const TernaryInput<double> &,
1362 float, double, RoundingMode);
1363 template bool compare_ternary_operation_one_output(Operation,
1364 const TernaryInput<double> &,
1365 double, double,
1366 RoundingMode);
1367 template bool compare_ternary_operation_one_output(
1368 Operation, const TernaryInput<long double> &, float, double, RoundingMode);
1369 template bool compare_ternary_operation_one_output(
1370 Operation, const TernaryInput<long double> &, double, double, RoundingMode);
1371 template bool
1372 compare_ternary_operation_one_output(Operation,
1373 const TernaryInput<long double> &,
1374 long double, double, RoundingMode);
1375
1376 #ifdef LIBC_TYPES_HAS_FLOAT16
1377 template bool compare_ternary_operation_one_output(Operation,
1378 const TernaryInput<float> &,
1379 float16, double,
1380 RoundingMode);
1381 template bool compare_ternary_operation_one_output(Operation,
1382 const TernaryInput<double> &,
1383 float16, double,
1384 RoundingMode);
1385 template bool
1386 compare_ternary_operation_one_output(Operation,
1387 const TernaryInput<long double> &, float16,
1388 double, RoundingMode);
1389 #endif
1390
1391 } // namespace internal
1392
round_to_long(T x,long & result)1393 template <typename T> bool round_to_long(T x, long &result) {
1394 MPFRNumber mpfr(x);
1395 return mpfr.round_to_long(result);
1396 }
1397
1398 template bool round_to_long<float>(float, long &);
1399 template bool round_to_long<double>(double, long &);
1400 template bool round_to_long<long double>(long double, long &);
1401 #ifdef LIBC_TYPES_HAS_FLOAT16
1402 template bool round_to_long<float16>(float16, long &);
1403 #endif
1404
round_to_long(T x,RoundingMode mode,long & result)1405 template <typename T> bool round_to_long(T x, RoundingMode mode, long &result) {
1406 MPFRNumber mpfr(x);
1407 return mpfr.round_to_long(get_mpfr_rounding_mode(mode), result);
1408 }
1409
1410 template bool round_to_long<float>(float, RoundingMode, long &);
1411 template bool round_to_long<double>(double, RoundingMode, long &);
1412 template bool round_to_long<long double>(long double, RoundingMode, long &);
1413 #ifdef LIBC_TYPES_HAS_FLOAT16
1414 template bool round_to_long<float16>(float16, RoundingMode, long &);
1415 #endif
1416
round(T x,RoundingMode mode)1417 template <typename T> T round(T x, RoundingMode mode) {
1418 MPFRNumber mpfr(x);
1419 MPFRNumber result = mpfr.rint(get_mpfr_rounding_mode(mode));
1420 return result.as<T>();
1421 }
1422
1423 template float round<float>(float, RoundingMode);
1424 template double round<double>(double, RoundingMode);
1425 template long double round<long double>(long double, RoundingMode);
1426 #ifdef LIBC_TYPES_HAS_FLOAT16
1427 template float16 round<float16>(float16, RoundingMode);
1428 #endif
1429
1430 } // namespace mpfr
1431 } // namespace testing
1432 } // namespace LIBC_NAMESPACE_DECL
1433