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