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