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 "utils/FPUtil/FPBits.h" 12 #include "utils/FPUtil/TestHelpers.h" 13 14 #include "llvm/ADT/StringExtras.h" 15 #include "llvm/ADT/StringRef.h" 16 17 #include <memory> 18 #include <stdint.h> 19 #include <string> 20 21 #ifdef CUSTOM_MPFR_INCLUDER 22 // Some downstream repos are monoliths carrying MPFR sources in their third 23 // party directory. In such repos, including the MPFR header as 24 // `#include <mpfr.h>` is either disallowed or not possible. If that is the 25 // case, a file named `CustomMPFRIncluder.h` should be added through which the 26 // MPFR header can be included in manner allowed in that repo. 27 #include "CustomMPFRIncluder.h" 28 #else 29 #include <mpfr.h> 30 #endif 31 32 template <typename T> using FPBits = __llvm_libc::fputil::FPBits<T>; 33 34 namespace __llvm_libc { 35 namespace testing { 36 namespace mpfr { 37 38 class MPFRNumber { 39 // A precision value which allows sufficiently large additional 40 // precision even compared to quad-precision floating point values. 41 static constexpr unsigned int mpfrPrecision = 128; 42 43 mpfr_t value; 44 45 public: 46 MPFRNumber() { mpfr_init2(value, mpfrPrecision); } 47 48 // We use explicit EnableIf specializations to disallow implicit 49 // conversions. Implicit conversions can potentially lead to loss of 50 // precision. 51 template <typename XType, 52 cpp::EnableIfType<cpp::IsSame<float, XType>::Value, int> = 0> 53 explicit MPFRNumber(XType x) { 54 mpfr_init2(value, mpfrPrecision); 55 mpfr_set_flt(value, x, MPFR_RNDN); 56 } 57 58 template <typename XType, 59 cpp::EnableIfType<cpp::IsSame<double, XType>::Value, int> = 0> 60 explicit MPFRNumber(XType x) { 61 mpfr_init2(value, mpfrPrecision); 62 mpfr_set_d(value, x, MPFR_RNDN); 63 } 64 65 template <typename XType, 66 cpp::EnableIfType<cpp::IsSame<long double, XType>::Value, int> = 0> 67 explicit MPFRNumber(XType x) { 68 mpfr_init2(value, mpfrPrecision); 69 mpfr_set_ld(value, x, MPFR_RNDN); 70 } 71 72 template <typename XType, 73 cpp::EnableIfType<cpp::IsIntegral<XType>::Value, int> = 0> 74 explicit MPFRNumber(XType x) { 75 mpfr_init2(value, mpfrPrecision); 76 mpfr_set_sj(value, x, MPFR_RNDN); 77 } 78 79 MPFRNumber(const MPFRNumber &other) { 80 mpfr_set(value, other.value, MPFR_RNDN); 81 } 82 83 ~MPFRNumber() { 84 mpfr_clear(value); 85 } 86 87 MPFRNumber &operator=(const MPFRNumber &rhs) { 88 mpfr_set(value, rhs.value, MPFR_RNDN); 89 return *this; 90 } 91 92 MPFRNumber abs() const { 93 MPFRNumber result; 94 mpfr_abs(result.value, value, MPFR_RNDN); 95 return result; 96 } 97 98 MPFRNumber ceil() const { 99 MPFRNumber result; 100 mpfr_ceil(result.value, value); 101 return result; 102 } 103 104 MPFRNumber cos() const { 105 MPFRNumber result; 106 mpfr_cos(result.value, value, MPFR_RNDN); 107 return result; 108 } 109 110 MPFRNumber exp() const { 111 MPFRNumber result; 112 mpfr_exp(result.value, value, MPFR_RNDN); 113 return result; 114 } 115 116 MPFRNumber exp2() const { 117 MPFRNumber result; 118 mpfr_exp2(result.value, value, MPFR_RNDN); 119 return result; 120 } 121 122 MPFRNumber floor() const { 123 MPFRNumber result; 124 mpfr_floor(result.value, value); 125 return result; 126 } 127 128 MPFRNumber frexp(int &exp) { 129 MPFRNumber result; 130 mpfr_exp_t resultExp; 131 mpfr_frexp(&resultExp, result.value, value, MPFR_RNDN); 132 exp = resultExp; 133 return result; 134 } 135 136 MPFRNumber hypot(const MPFRNumber &b) { 137 MPFRNumber result; 138 mpfr_hypot(result.value, value, b.value, MPFR_RNDN); 139 return result; 140 } 141 142 MPFRNumber remquo(const MPFRNumber &divisor, int "ient) { 143 MPFRNumber remainder; 144 long q; 145 mpfr_remquo(remainder.value, &q, value, divisor.value, MPFR_RNDN); 146 quotient = q; 147 return remainder; 148 } 149 150 MPFRNumber round() const { 151 MPFRNumber result; 152 mpfr_round(result.value, value); 153 return result; 154 } 155 156 bool roundToLong(long &result) const { 157 // We first calculate the rounded value. This way, when converting 158 // to long using mpfr_get_si, the rounding direction of MPFR_RNDN 159 // (or any other rounding mode), does not have an influence. 160 MPFRNumber roundedValue = round(); 161 mpfr_clear_erangeflag(); 162 result = mpfr_get_si(roundedValue.value, MPFR_RNDN); 163 return mpfr_erangeflag_p(); 164 } 165 166 bool roundToLong(mpfr_rnd_t rnd, long &result) const { 167 MPFRNumber rint_result; 168 mpfr_rint(rint_result.value, value, rnd); 169 return rint_result.roundToLong(result); 170 } 171 172 MPFRNumber rint(mpfr_rnd_t rnd) const { 173 MPFRNumber result; 174 mpfr_rint(result.value, value, rnd); 175 return result; 176 } 177 178 MPFRNumber sin() const { 179 MPFRNumber result; 180 mpfr_sin(result.value, value, MPFR_RNDN); 181 return result; 182 } 183 184 MPFRNumber sqrt() const { 185 MPFRNumber result; 186 mpfr_sqrt(result.value, value, MPFR_RNDN); 187 return result; 188 } 189 190 MPFRNumber trunc() const { 191 MPFRNumber result; 192 mpfr_trunc(result.value, value); 193 return result; 194 } 195 196 std::string str() const { 197 // 200 bytes should be more than sufficient to hold a 100-digit number 198 // plus additional bytes for the decimal point, '-' sign etc. 199 constexpr size_t printBufSize = 200; 200 char buffer[printBufSize]; 201 mpfr_snprintf(buffer, printBufSize, "%100.50Rf", value); 202 llvm::StringRef ref(buffer); 203 ref = ref.trim(); 204 return ref.str(); 205 } 206 207 // These functions are useful for debugging. 208 template <typename T> T as() const; 209 210 template <> float as<float>() const { return mpfr_get_flt(value, MPFR_RNDN); } 211 template <> double as<double>() const { return mpfr_get_d(value, MPFR_RNDN); } 212 template <> long double as<long double>() const { 213 return mpfr_get_ld(value, MPFR_RNDN); 214 } 215 216 void dump(const char *msg) const { mpfr_printf("%s%.128Rf\n", msg, value); } 217 218 // Return the ULP (units-in-the-last-place) difference between the 219 // stored MPFR and a floating point number. 220 // 221 // We define: 222 // ULP(mpfr_value, value) = abs(mpfr_value - value) / eps(value) 223 // 224 // Remarks: 225 // 1. ULP < 0.5 will imply that the value is correctly rounded. 226 // 2. We expect that this value and the value to be compared (the [input] 227 // argument) are reasonable close, and we will provide an upper bound 228 // of ULP value for testing. Morever, most of the fractional parts of 229 // ULP value do not matter much, so using double as the return type 230 // should be good enough. 231 template <typename T> 232 cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, double> ulp(T input) { 233 fputil::FPBits<T> bits(input); 234 MPFRNumber mpfrInput(input); 235 236 // abs(value - input) 237 mpfr_sub(mpfrInput.value, value, mpfrInput.value, MPFR_RNDN); 238 mpfr_abs(mpfrInput.value, mpfrInput.value, MPFR_RNDN); 239 240 // get eps(input) 241 int epsExponent = bits.exponent - fputil::FPBits<T>::exponentBias - 242 fputil::MantissaWidth<T>::value; 243 if (bits.exponent == 0) { 244 // correcting denormal exponent 245 ++epsExponent; 246 } else if ((bits.mantissa == 0) && (bits.exponent > 1) && 247 mpfr_less_p(value, mpfrInput.value)) { 248 // when the input is exactly 2^n, distance (epsilon) between the input 249 // and the next floating point number is different from the distance to 250 // the previous floating point number. So in that case, if the correct 251 // value from MPFR is smaller than the input, we use the smaller epsilon 252 --epsExponent; 253 } 254 255 // Since eps(value) is of the form 2^e, instead of dividing such number, 256 // we multiply by its inverse 2^{-e}. 257 mpfr_mul_2si(mpfrInput.value, mpfrInput.value, -epsExponent, MPFR_RNDN); 258 259 return mpfrInput.as<double>(); 260 } 261 }; 262 263 namespace internal { 264 265 template <typename InputType> 266 cpp::EnableIfType<cpp::IsFloatingPointType<InputType>::Value, MPFRNumber> 267 unaryOperation(Operation op, InputType input) { 268 MPFRNumber mpfrInput(input); 269 switch (op) { 270 case Operation::Abs: 271 return mpfrInput.abs(); 272 case Operation::Ceil: 273 return mpfrInput.ceil(); 274 case Operation::Cos: 275 return mpfrInput.cos(); 276 case Operation::Exp: 277 return mpfrInput.exp(); 278 case Operation::Exp2: 279 return mpfrInput.exp2(); 280 case Operation::Floor: 281 return mpfrInput.floor(); 282 case Operation::Round: 283 return mpfrInput.round(); 284 case Operation::Sin: 285 return mpfrInput.sin(); 286 case Operation::Sqrt: 287 return mpfrInput.sqrt(); 288 case Operation::Trunc: 289 return mpfrInput.trunc(); 290 default: 291 __builtin_unreachable(); 292 } 293 } 294 295 template <typename InputType> 296 cpp::EnableIfType<cpp::IsFloatingPointType<InputType>::Value, MPFRNumber> 297 unaryOperationTwoOutputs(Operation op, InputType input, int &output) { 298 MPFRNumber mpfrInput(input); 299 switch (op) { 300 case Operation::Frexp: 301 return mpfrInput.frexp(output); 302 default: 303 __builtin_unreachable(); 304 } 305 } 306 307 template <typename InputType> 308 cpp::EnableIfType<cpp::IsFloatingPointType<InputType>::Value, MPFRNumber> 309 binaryOperationOneOutput(Operation op, InputType x, InputType y) { 310 MPFRNumber inputX(x), inputY(y); 311 switch (op) { 312 case Operation::Hypot: 313 return inputX.hypot(inputY); 314 default: 315 __builtin_unreachable(); 316 } 317 } 318 319 template <typename InputType> 320 cpp::EnableIfType<cpp::IsFloatingPointType<InputType>::Value, MPFRNumber> 321 binaryOperationTwoOutputs(Operation op, InputType x, InputType y, int &output) { 322 MPFRNumber inputX(x), inputY(y); 323 switch (op) { 324 case Operation::RemQuo: 325 return inputX.remquo(inputY, output); 326 default: 327 __builtin_unreachable(); 328 } 329 } 330 331 template <typename T> 332 void explainUnaryOperationSingleOutputError(Operation op, T input, T matchValue, 333 testutils::StreamWrapper &OS) { 334 MPFRNumber mpfrInput(input); 335 MPFRNumber mpfrResult = unaryOperation(op, input); 336 MPFRNumber mpfrMatchValue(matchValue); 337 FPBits<T> inputBits(input); 338 FPBits<T> matchBits(matchValue); 339 FPBits<T> mpfrResultBits(mpfrResult.as<T>()); 340 OS << "Match value not within tolerance value of MPFR result:\n" 341 << " Input decimal: " << mpfrInput.str() << '\n'; 342 __llvm_libc::fputil::testing::describeValue(" Input bits: ", input, OS); 343 OS << '\n' << " Match decimal: " << mpfrMatchValue.str() << '\n'; 344 __llvm_libc::fputil::testing::describeValue(" Match bits: ", matchValue, 345 OS); 346 OS << '\n' << " MPFR result: " << mpfrResult.str() << '\n'; 347 __llvm_libc::fputil::testing::describeValue( 348 " MPFR rounded: ", mpfrResult.as<T>(), OS); 349 OS << '\n'; 350 OS << " ULP error: " << std::to_string(mpfrResult.ulp(matchValue)) 351 << '\n'; 352 } 353 354 template void 355 explainUnaryOperationSingleOutputError<float>(Operation op, float, float, 356 testutils::StreamWrapper &); 357 template void 358 explainUnaryOperationSingleOutputError<double>(Operation op, double, double, 359 testutils::StreamWrapper &); 360 template void explainUnaryOperationSingleOutputError<long double>( 361 Operation op, long double, long double, testutils::StreamWrapper &); 362 363 template <typename T> 364 void explainUnaryOperationTwoOutputsError(Operation op, T input, 365 const BinaryOutput<T> &libcResult, 366 testutils::StreamWrapper &OS) { 367 MPFRNumber mpfrInput(input); 368 FPBits<T> inputBits(input); 369 int mpfrIntResult; 370 MPFRNumber mpfrResult = unaryOperationTwoOutputs(op, input, mpfrIntResult); 371 372 if (mpfrIntResult != libcResult.i) { 373 OS << "MPFR integral result: " << mpfrIntResult << '\n' 374 << "Libc integral result: " << libcResult.i << '\n'; 375 } else { 376 OS << "Integral result from libc matches integral result from MPFR.\n"; 377 } 378 379 MPFRNumber mpfrMatchValue(libcResult.f); 380 OS << "Libc floating point result is not within tolerance value of the MPFR " 381 << "result.\n\n"; 382 383 OS << " Input decimal: " << mpfrInput.str() << "\n\n"; 384 385 OS << "Libc floating point value: " << mpfrMatchValue.str() << '\n'; 386 __llvm_libc::fputil::testing::describeValue( 387 " Libc floating point bits: ", libcResult.f, OS); 388 OS << "\n\n"; 389 390 OS << " MPFR result: " << mpfrResult.str() << '\n'; 391 __llvm_libc::fputil::testing::describeValue( 392 " MPFR rounded: ", mpfrResult.as<T>(), OS); 393 OS << '\n' 394 << " ULP error: " 395 << std::to_string(mpfrResult.ulp(libcResult.f)) << '\n'; 396 } 397 398 template void explainUnaryOperationTwoOutputsError<float>( 399 Operation, float, const BinaryOutput<float> &, testutils::StreamWrapper &); 400 template void 401 explainUnaryOperationTwoOutputsError<double>(Operation, double, 402 const BinaryOutput<double> &, 403 testutils::StreamWrapper &); 404 template void explainUnaryOperationTwoOutputsError<long double>( 405 Operation, long double, const BinaryOutput<long double> &, 406 testutils::StreamWrapper &); 407 408 template <typename T> 409 void explainBinaryOperationTwoOutputsError(Operation op, 410 const BinaryInput<T> &input, 411 const BinaryOutput<T> &libcResult, 412 testutils::StreamWrapper &OS) { 413 MPFRNumber mpfrX(input.x); 414 MPFRNumber mpfrY(input.y); 415 FPBits<T> xbits(input.x); 416 FPBits<T> ybits(input.y); 417 int mpfrIntResult; 418 MPFRNumber mpfrResult = 419 binaryOperationTwoOutputs(op, input.x, input.y, mpfrIntResult); 420 MPFRNumber mpfrMatchValue(libcResult.f); 421 422 OS << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n' 423 << "MPFR integral result: " << mpfrIntResult << '\n' 424 << "Libc integral result: " << libcResult.i << '\n' 425 << "Libc floating point result: " << mpfrMatchValue.str() << '\n' 426 << " MPFR result: " << mpfrResult.str() << '\n'; 427 __llvm_libc::fputil::testing::describeValue( 428 "Libc floating point result bits: ", libcResult.f, OS); 429 __llvm_libc::fputil::testing::describeValue( 430 " MPFR rounded bits: ", mpfrResult.as<T>(), OS); 431 OS << "ULP error: " << std::to_string(mpfrResult.ulp(libcResult.f)) << '\n'; 432 } 433 434 template void explainBinaryOperationTwoOutputsError<float>( 435 Operation, const BinaryInput<float> &, const BinaryOutput<float> &, 436 testutils::StreamWrapper &); 437 template void explainBinaryOperationTwoOutputsError<double>( 438 Operation, const BinaryInput<double> &, const BinaryOutput<double> &, 439 testutils::StreamWrapper &); 440 template void explainBinaryOperationTwoOutputsError<long double>( 441 Operation, const BinaryInput<long double> &, 442 const BinaryOutput<long double> &, testutils::StreamWrapper &); 443 444 template <typename T> 445 void explainBinaryOperationOneOutputError(Operation op, 446 const BinaryInput<T> &input, 447 T libcResult, 448 testutils::StreamWrapper &OS) { 449 MPFRNumber mpfrX(input.x); 450 MPFRNumber mpfrY(input.y); 451 FPBits<T> xbits(input.x); 452 FPBits<T> ybits(input.y); 453 MPFRNumber mpfrResult = binaryOperationOneOutput(op, input.x, input.y); 454 MPFRNumber mpfrMatchValue(libcResult); 455 456 OS << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n'; 457 __llvm_libc::fputil::testing::describeValue("First input bits: ", input.x, 458 OS); 459 __llvm_libc::fputil::testing::describeValue("Second input bits: ", input.y, 460 OS); 461 462 OS << "Libc result: " << mpfrMatchValue.str() << '\n' 463 << "MPFR result: " << mpfrResult.str() << '\n'; 464 __llvm_libc::fputil::testing::describeValue( 465 "Libc floating point result bits: ", libcResult, OS); 466 __llvm_libc::fputil::testing::describeValue( 467 " MPFR rounded bits: ", mpfrResult.as<T>(), OS); 468 OS << "ULP error: " << std::to_string(mpfrResult.ulp(libcResult)) << '\n'; 469 } 470 471 template void explainBinaryOperationOneOutputError<float>( 472 Operation, const BinaryInput<float> &, float, testutils::StreamWrapper &); 473 template void explainBinaryOperationOneOutputError<double>( 474 Operation, const BinaryInput<double> &, double, testutils::StreamWrapper &); 475 template void explainBinaryOperationOneOutputError<long double>( 476 Operation, const BinaryInput<long double> &, long double, 477 testutils::StreamWrapper &); 478 479 template <typename T> 480 bool compareUnaryOperationSingleOutput(Operation op, T input, T libcResult, 481 double ulpError) { 482 // If the ulp error is exactly 0.5 (i.e a tie), we would check that the result 483 // is rounded to the nearest even. 484 MPFRNumber mpfrResult = unaryOperation(op, input); 485 double ulp = mpfrResult.ulp(libcResult); 486 bool bitsAreEven = ((FPBits<T>(libcResult).bitsAsUInt() & 1) == 0); 487 return (ulp < ulpError) || 488 ((ulp == ulpError) && ((ulp != 0.5) || bitsAreEven)); 489 } 490 491 template bool compareUnaryOperationSingleOutput<float>(Operation, float, float, 492 double); 493 template bool compareUnaryOperationSingleOutput<double>(Operation, double, 494 double, double); 495 template bool compareUnaryOperationSingleOutput<long double>(Operation, 496 long double, 497 long double, 498 double); 499 500 template <typename T> 501 bool compareUnaryOperationTwoOutputs(Operation op, T input, 502 const BinaryOutput<T> &libcResult, 503 double ulpError) { 504 int mpfrIntResult; 505 MPFRNumber mpfrResult = unaryOperationTwoOutputs(op, input, mpfrIntResult); 506 double ulp = mpfrResult.ulp(libcResult.f); 507 508 if (mpfrIntResult != libcResult.i) 509 return false; 510 511 bool bitsAreEven = ((FPBits<T>(libcResult.f).bitsAsUInt() & 1) == 0); 512 return (ulp < ulpError) || 513 ((ulp == ulpError) && ((ulp != 0.5) || bitsAreEven)); 514 } 515 516 template bool 517 compareUnaryOperationTwoOutputs<float>(Operation, float, 518 const BinaryOutput<float> &, double); 519 template bool 520 compareUnaryOperationTwoOutputs<double>(Operation, double, 521 const BinaryOutput<double> &, double); 522 template bool compareUnaryOperationTwoOutputs<long double>( 523 Operation, long double, const BinaryOutput<long double> &, double); 524 525 template <typename T> 526 bool compareBinaryOperationTwoOutputs(Operation op, const BinaryInput<T> &input, 527 const BinaryOutput<T> &libcResult, 528 double ulpError) { 529 int mpfrIntResult; 530 MPFRNumber mpfrResult = 531 binaryOperationTwoOutputs(op, input.x, input.y, mpfrIntResult); 532 double ulp = mpfrResult.ulp(libcResult.f); 533 534 if (mpfrIntResult != libcResult.i) { 535 if (op == Operation::RemQuo) { 536 if ((0x7 & mpfrIntResult) != (0x7 & libcResult.i)) 537 return false; 538 } else { 539 return false; 540 } 541 } 542 543 bool bitsAreEven = ((FPBits<T>(libcResult.f).bitsAsUInt() & 1) == 0); 544 return (ulp < ulpError) || 545 ((ulp == ulpError) && ((ulp != 0.5) || bitsAreEven)); 546 } 547 548 template bool 549 compareBinaryOperationTwoOutputs<float>(Operation, const BinaryInput<float> &, 550 const BinaryOutput<float> &, double); 551 template bool 552 compareBinaryOperationTwoOutputs<double>(Operation, const BinaryInput<double> &, 553 const BinaryOutput<double> &, double); 554 template bool compareBinaryOperationTwoOutputs<long double>( 555 Operation, const BinaryInput<long double> &, 556 const BinaryOutput<long double> &, double); 557 558 template <typename T> 559 bool compareBinaryOperationOneOutput(Operation op, const BinaryInput<T> &input, 560 T libcResult, double ulpError) { 561 MPFRNumber mpfrResult = binaryOperationOneOutput(op, input.x, input.y); 562 double ulp = mpfrResult.ulp(libcResult); 563 564 bool bitsAreEven = ((FPBits<T>(libcResult).bitsAsUInt() & 1) == 0); 565 return (ulp < ulpError) || 566 ((ulp == ulpError) && ((ulp != 0.5) || bitsAreEven)); 567 } 568 569 template bool compareBinaryOperationOneOutput<float>(Operation, 570 const BinaryInput<float> &, 571 float, double); 572 template bool 573 compareBinaryOperationOneOutput<double>(Operation, const BinaryInput<double> &, 574 double, double); 575 template bool compareBinaryOperationOneOutput<long double>( 576 Operation, const BinaryInput<long double> &, long double, double); 577 578 static mpfr_rnd_t getMPFRRoundingMode(RoundingMode mode) { 579 switch (mode) { 580 case RoundingMode::Upward: 581 return MPFR_RNDU; 582 break; 583 case RoundingMode::Downward: 584 return MPFR_RNDD; 585 break; 586 case RoundingMode::TowardZero: 587 return MPFR_RNDZ; 588 break; 589 case RoundingMode::Nearest: 590 return MPFR_RNDN; 591 break; 592 } 593 } 594 595 } // namespace internal 596 597 template <typename T> bool RoundToLong(T x, long &result) { 598 MPFRNumber mpfr(x); 599 return mpfr.roundToLong(result); 600 } 601 602 template bool RoundToLong<float>(float, long &); 603 template bool RoundToLong<double>(double, long &); 604 template bool RoundToLong<long double>(long double, long &); 605 606 template <typename T> bool RoundToLong(T x, RoundingMode mode, long &result) { 607 MPFRNumber mpfr(x); 608 return mpfr.roundToLong(internal::getMPFRRoundingMode(mode), result); 609 } 610 611 template bool RoundToLong<float>(float, RoundingMode, long &); 612 template bool RoundToLong<double>(double, RoundingMode, long &); 613 template bool RoundToLong<long double>(long double, RoundingMode, long &); 614 615 template <typename T> T Round(T x, RoundingMode mode) { 616 MPFRNumber mpfr(x); 617 MPFRNumber result = mpfr.rint(internal::getMPFRRoundingMode(mode)); 618 return result.as<T>(); 619 } 620 621 template float Round<float>(float, RoundingMode); 622 template double Round<double>(double, RoundingMode); 623 template long double Round<long double>(long double, RoundingMode); 624 625 } // namespace mpfr 626 } // namespace testing 627 } // namespace __llvm_libc 628