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