//===-- Utils which wrap MPFR ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "MPFRUtils.h" #include "src/__support/FPUtil/FPBits.h" #include "src/__support/FPUtil/TestHelpers.h" #include "utils/CPP/StringView.h" #include #include #include #include #ifdef CUSTOM_MPFR_INCLUDER // Some downstream repos are monoliths carrying MPFR sources in their third // party directory. In such repos, including the MPFR header as // `#include ` is either disallowed or not possible. If that is the // case, a file named `CustomMPFRIncluder.h` should be added through which the // MPFR header can be included in manner allowed in that repo. #include "CustomMPFRIncluder.h" #else #include #endif template using FPBits = __llvm_libc::fputil::FPBits; namespace __llvm_libc { namespace testing { namespace mpfr { template struct Precision; template <> struct Precision { static constexpr unsigned int value = 24; }; template <> struct Precision { static constexpr unsigned int value = 53; }; #if !(defined(__x86_64__) || defined(__i386__)) template <> struct Precision { static constexpr unsigned int value = 64; }; #else template <> struct Precision { static constexpr unsigned int value = 113; }; #endif class MPFRNumber { // A precision value which allows sufficiently large additional // precision even compared to quad-precision floating point values. unsigned int mpfrPrecision; mpfr_t value; public: MPFRNumber() : mpfrPrecision(256) { mpfr_init2(value, mpfrPrecision); } // We use explicit EnableIf specializations to disallow implicit // conversions. Implicit conversions can potentially lead to loss of // precision. template ::Value, int> = 0> explicit MPFRNumber(XType x, int precision = 128) : mpfrPrecision(precision) { mpfr_init2(value, mpfrPrecision); mpfr_set_flt(value, x, MPFR_RNDN); } template ::Value, int> = 0> explicit MPFRNumber(XType x, int precision = 128) : mpfrPrecision(precision) { mpfr_init2(value, mpfrPrecision); mpfr_set_d(value, x, MPFR_RNDN); } template ::Value, int> = 0> explicit MPFRNumber(XType x, int precision = 128) : mpfrPrecision(precision) { mpfr_init2(value, mpfrPrecision); mpfr_set_ld(value, x, MPFR_RNDN); } template ::Value, int> = 0> explicit MPFRNumber(XType x, int precision = 128) : mpfrPrecision(precision) { mpfr_init2(value, mpfrPrecision); mpfr_set_sj(value, x, MPFR_RNDN); } MPFRNumber(const MPFRNumber &other) : mpfrPrecision(other.mpfrPrecision) { mpfr_init2(value, mpfrPrecision); mpfr_set(value, other.value, MPFR_RNDN); } ~MPFRNumber() { mpfr_clear(value); } MPFRNumber &operator=(const MPFRNumber &rhs) { mpfrPrecision = rhs.mpfrPrecision; mpfr_set(value, rhs.value, MPFR_RNDN); return *this; } MPFRNumber abs() const { MPFRNumber result; mpfr_abs(result.value, value, MPFR_RNDN); return result; } MPFRNumber ceil() const { MPFRNumber result; mpfr_ceil(result.value, value); return result; } MPFRNumber cos() const { MPFRNumber result; mpfr_cos(result.value, value, MPFR_RNDN); return result; } MPFRNumber exp() const { MPFRNumber result; mpfr_exp(result.value, value, MPFR_RNDN); return result; } MPFRNumber exp2() const { MPFRNumber result; mpfr_exp2(result.value, value, MPFR_RNDN); return result; } MPFRNumber expm1() const { MPFRNumber result; mpfr_expm1(result.value, value, MPFR_RNDN); return result; } MPFRNumber floor() const { MPFRNumber result; mpfr_floor(result.value, value); return result; } MPFRNumber frexp(int &exp) { MPFRNumber result; mpfr_exp_t resultExp; mpfr_frexp(&resultExp, result.value, value, MPFR_RNDN); exp = resultExp; return result; } MPFRNumber hypot(const MPFRNumber &b) { MPFRNumber result; mpfr_hypot(result.value, value, b.value, MPFR_RNDN); return result; } MPFRNumber remquo(const MPFRNumber &divisor, int "ient) { MPFRNumber remainder; long q; mpfr_remquo(remainder.value, &q, value, divisor.value, MPFR_RNDN); quotient = q; return remainder; } MPFRNumber round() const { MPFRNumber result; mpfr_round(result.value, value); return result; } bool roundToLong(long &result) const { // We first calculate the rounded value. This way, when converting // to long using mpfr_get_si, the rounding direction of MPFR_RNDN // (or any other rounding mode), does not have an influence. MPFRNumber roundedValue = round(); mpfr_clear_erangeflag(); result = mpfr_get_si(roundedValue.value, MPFR_RNDN); return mpfr_erangeflag_p(); } bool roundToLong(mpfr_rnd_t rnd, long &result) const { MPFRNumber rint_result; mpfr_rint(rint_result.value, value, rnd); return rint_result.roundToLong(result); } MPFRNumber rint(mpfr_rnd_t rnd) const { MPFRNumber result; mpfr_rint(result.value, value, rnd); return result; } MPFRNumber mod_2pi() const { MPFRNumber result(0.0, 1280); MPFRNumber _2pi(0.0, 1280); mpfr_const_pi(_2pi.value, MPFR_RNDN); mpfr_mul_si(_2pi.value, _2pi.value, 2, MPFR_RNDN); mpfr_fmod(result.value, value, _2pi.value, MPFR_RNDN); return result; } MPFRNumber mod_pi_over_2() const { MPFRNumber result(0.0, 1280); MPFRNumber pi_over_2(0.0, 1280); mpfr_const_pi(pi_over_2.value, MPFR_RNDN); mpfr_mul_d(pi_over_2.value, pi_over_2.value, 0.5, MPFR_RNDN); mpfr_fmod(result.value, value, pi_over_2.value, MPFR_RNDN); return result; } MPFRNumber mod_pi_over_4() const { MPFRNumber result(0.0, 1280); MPFRNumber pi_over_4(0.0, 1280); mpfr_const_pi(pi_over_4.value, MPFR_RNDN); mpfr_mul_d(pi_over_4.value, pi_over_4.value, 0.25, MPFR_RNDN); mpfr_fmod(result.value, value, pi_over_4.value, MPFR_RNDN); return result; } MPFRNumber sin() const { MPFRNumber result; mpfr_sin(result.value, value, MPFR_RNDN); return result; } MPFRNumber sqrt() const { MPFRNumber result; mpfr_sqrt(result.value, value, MPFR_RNDN); return result; } MPFRNumber tan() const { MPFRNumber result; mpfr_tan(result.value, value, MPFR_RNDN); return result; } MPFRNumber trunc() const { MPFRNumber result; mpfr_trunc(result.value, value); return result; } MPFRNumber fma(const MPFRNumber &b, const MPFRNumber &c) { MPFRNumber result(*this); mpfr_fma(result.value, value, b.value, c.value, MPFR_RNDN); return result; } std::string str() const { // 200 bytes should be more than sufficient to hold a 100-digit number // plus additional bytes for the decimal point, '-' sign etc. constexpr size_t printBufSize = 200; char buffer[printBufSize]; mpfr_snprintf(buffer, printBufSize, "%100.50Rf", value); cpp::StringView view(buffer); view = view.trim(' '); return std::string(view.data()); } // These functions are useful for debugging. template T as() const; template <> float as() const { return mpfr_get_flt(value, MPFR_RNDN); } template <> double as() const { return mpfr_get_d(value, MPFR_RNDN); } template <> long double as() const { return mpfr_get_ld(value, MPFR_RNDN); } void dump(const char *msg) const { mpfr_printf("%s%.128Rf\n", msg, value); } // Return the ULP (units-in-the-last-place) difference between the // stored MPFR and a floating point number. // // We define ULP difference as follows: // If exponents of this value and the |input| are same, then: // ULP(this_value, input) = abs(this_value - input) / eps(input) // else: // max = max(abs(this_value), abs(input)) // min = min(abs(this_value), abs(input)) // maxExponent = exponent(max) // ULP(this_value, input) = (max - 2^maxExponent) / eps(max) + // (2^maxExponent - min) / eps(min) // // Remarks: // 1. A ULP of 0.0 will imply that the value is correctly rounded. // 2. We expect that this value and the value to be compared (the [input] // argument) are reasonable close, and we will provide an upper bound // of ULP value for testing. Morever, most of the fractional parts of // ULP value do not matter much, so using double as the return type // should be good enough. // 3. For close enough values (values which don't diff in their exponent by // not more than 1), a ULP difference of N indicates a bit distance // of N between this number and [input]. // 4. A values of +0.0 and -0.0 are treated as equal. template cpp::EnableIfType::Value, double> ulp(T input) { T thisAsT = as(); if (thisAsT == input) return T(0.0); int thisExponent = fputil::FPBits(thisAsT).getExponent(); int inputExponent = fputil::FPBits(input).getExponent(); if (thisAsT * input < 0 || thisExponent == inputExponent) { MPFRNumber inputMPFR(input); mpfr_sub(inputMPFR.value, value, inputMPFR.value, MPFR_RNDN); mpfr_abs(inputMPFR.value, inputMPFR.value, MPFR_RNDN); mpfr_mul_2si(inputMPFR.value, inputMPFR.value, -thisExponent, MPFR_RNDN); return inputMPFR.as(); } // If the control reaches here, it means that this number and input are // of the same sign but different exponent. In such a case, ULP error is // calculated as sum of two parts. thisAsT = std::abs(thisAsT); input = std::abs(input); T min = thisAsT > input ? input : thisAsT; T max = thisAsT > input ? thisAsT : input; int minExponent = fputil::FPBits(min).getExponent(); int maxExponent = fputil::FPBits(max).getExponent(); MPFRNumber minMPFR(min); MPFRNumber maxMPFR(max); MPFRNumber pivot(uint32_t(1)); mpfr_mul_2si(pivot.value, pivot.value, maxExponent, MPFR_RNDN); mpfr_sub(minMPFR.value, pivot.value, minMPFR.value, MPFR_RNDN); mpfr_mul_2si(minMPFR.value, minMPFR.value, -minExponent, MPFR_RNDN); mpfr_sub(maxMPFR.value, maxMPFR.value, pivot.value, MPFR_RNDN); mpfr_mul_2si(maxMPFR.value, maxMPFR.value, -maxExponent, MPFR_RNDN); mpfr_add(minMPFR.value, minMPFR.value, maxMPFR.value, MPFR_RNDN); return minMPFR.as(); } }; namespace internal { template cpp::EnableIfType::Value, MPFRNumber> unaryOperation(Operation op, InputType input) { MPFRNumber mpfrInput(input); switch (op) { case Operation::Abs: return mpfrInput.abs(); case Operation::Ceil: return mpfrInput.ceil(); case Operation::Cos: return mpfrInput.cos(); case Operation::Exp: return mpfrInput.exp(); case Operation::Exp2: return mpfrInput.exp2(); case Operation::Expm1: return mpfrInput.expm1(); case Operation::Floor: return mpfrInput.floor(); case Operation::Mod2PI: return mpfrInput.mod_2pi(); case Operation::ModPIOver2: return mpfrInput.mod_pi_over_2(); case Operation::ModPIOver4: return mpfrInput.mod_pi_over_4(); case Operation::Round: return mpfrInput.round(); case Operation::Sin: return mpfrInput.sin(); case Operation::Sqrt: return mpfrInput.sqrt(); case Operation::Tan: return mpfrInput.tan(); case Operation::Trunc: return mpfrInput.trunc(); default: __builtin_unreachable(); } } template cpp::EnableIfType::Value, MPFRNumber> unaryOperationTwoOutputs(Operation op, InputType input, int &output) { MPFRNumber mpfrInput(input); switch (op) { case Operation::Frexp: return mpfrInput.frexp(output); default: __builtin_unreachable(); } } template cpp::EnableIfType::Value, MPFRNumber> binaryOperationOneOutput(Operation op, InputType x, InputType y) { MPFRNumber inputX(x), inputY(y); switch (op) { case Operation::Hypot: return inputX.hypot(inputY); default: __builtin_unreachable(); } } template cpp::EnableIfType::Value, MPFRNumber> binaryOperationTwoOutputs(Operation op, InputType x, InputType y, int &output) { MPFRNumber inputX(x), inputY(y); switch (op) { case Operation::RemQuo: return inputX.remquo(inputY, output); default: __builtin_unreachable(); } } template cpp::EnableIfType::Value, MPFRNumber> ternaryOperationOneOutput(Operation op, InputType x, InputType y, InputType z) { // For FMA function, we just need to compare with the mpfr_fma with the same // precision as InputType. Using higher precision as the intermediate results // to compare might incorrectly fail due to double-rounding errors. constexpr unsigned int prec = Precision::value; MPFRNumber inputX(x, prec), inputY(y, prec), inputZ(z, prec); switch (op) { case Operation::Fma: return inputX.fma(inputY, inputZ); default: __builtin_unreachable(); } } template void explainUnaryOperationSingleOutputError(Operation op, T input, T matchValue, testutils::StreamWrapper &OS) { MPFRNumber mpfrInput(input); MPFRNumber mpfrResult = unaryOperation(op, input); MPFRNumber mpfrMatchValue(matchValue); FPBits inputBits(input); FPBits matchBits(matchValue); FPBits mpfrResultBits(mpfrResult.as()); OS << "Match value not within tolerance value of MPFR result:\n" << " Input decimal: " << mpfrInput.str() << '\n'; __llvm_libc::fputil::testing::describeValue(" Input bits: ", input, OS); OS << '\n' << " Match decimal: " << mpfrMatchValue.str() << '\n'; __llvm_libc::fputil::testing::describeValue(" Match bits: ", matchValue, OS); OS << '\n' << " MPFR result: " << mpfrResult.str() << '\n'; __llvm_libc::fputil::testing::describeValue( " MPFR rounded: ", mpfrResult.as(), OS); OS << '\n'; OS << " ULP error: " << std::to_string(mpfrResult.ulp(matchValue)) << '\n'; } template void explainUnaryOperationSingleOutputError(Operation op, float, float, testutils::StreamWrapper &); template void explainUnaryOperationSingleOutputError(Operation op, double, double, testutils::StreamWrapper &); template void explainUnaryOperationSingleOutputError( Operation op, long double, long double, testutils::StreamWrapper &); template void explainUnaryOperationTwoOutputsError(Operation op, T input, const BinaryOutput &libcResult, testutils::StreamWrapper &OS) { MPFRNumber mpfrInput(input); FPBits inputBits(input); int mpfrIntResult; MPFRNumber mpfrResult = unaryOperationTwoOutputs(op, input, mpfrIntResult); if (mpfrIntResult != libcResult.i) { OS << "MPFR integral result: " << mpfrIntResult << '\n' << "Libc integral result: " << libcResult.i << '\n'; } else { OS << "Integral result from libc matches integral result from MPFR.\n"; } MPFRNumber mpfrMatchValue(libcResult.f); OS << "Libc floating point result is not within tolerance value of the MPFR " << "result.\n\n"; OS << " Input decimal: " << mpfrInput.str() << "\n\n"; OS << "Libc floating point value: " << mpfrMatchValue.str() << '\n'; __llvm_libc::fputil::testing::describeValue( " Libc floating point bits: ", libcResult.f, OS); OS << "\n\n"; OS << " MPFR result: " << mpfrResult.str() << '\n'; __llvm_libc::fputil::testing::describeValue( " MPFR rounded: ", mpfrResult.as(), OS); OS << '\n' << " ULP error: " << std::to_string(mpfrResult.ulp(libcResult.f)) << '\n'; } template void explainUnaryOperationTwoOutputsError( Operation, float, const BinaryOutput &, testutils::StreamWrapper &); template void explainUnaryOperationTwoOutputsError(Operation, double, const BinaryOutput &, testutils::StreamWrapper &); template void explainUnaryOperationTwoOutputsError( Operation, long double, const BinaryOutput &, testutils::StreamWrapper &); template void explainBinaryOperationTwoOutputsError(Operation op, const BinaryInput &input, const BinaryOutput &libcResult, testutils::StreamWrapper &OS) { MPFRNumber mpfrX(input.x); MPFRNumber mpfrY(input.y); FPBits xbits(input.x); FPBits ybits(input.y); int mpfrIntResult; MPFRNumber mpfrResult = binaryOperationTwoOutputs(op, input.x, input.y, mpfrIntResult); MPFRNumber mpfrMatchValue(libcResult.f); OS << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n' << "MPFR integral result: " << mpfrIntResult << '\n' << "Libc integral result: " << libcResult.i << '\n' << "Libc floating point result: " << mpfrMatchValue.str() << '\n' << " MPFR result: " << mpfrResult.str() << '\n'; __llvm_libc::fputil::testing::describeValue( "Libc floating point result bits: ", libcResult.f, OS); __llvm_libc::fputil::testing::describeValue( " MPFR rounded bits: ", mpfrResult.as(), OS); OS << "ULP error: " << std::to_string(mpfrResult.ulp(libcResult.f)) << '\n'; } template void explainBinaryOperationTwoOutputsError( Operation, const BinaryInput &, const BinaryOutput &, testutils::StreamWrapper &); template void explainBinaryOperationTwoOutputsError( Operation, const BinaryInput &, const BinaryOutput &, testutils::StreamWrapper &); template void explainBinaryOperationTwoOutputsError( Operation, const BinaryInput &, const BinaryOutput &, testutils::StreamWrapper &); template void explainBinaryOperationOneOutputError(Operation op, const BinaryInput &input, T libcResult, testutils::StreamWrapper &OS) { MPFRNumber mpfrX(input.x); MPFRNumber mpfrY(input.y); FPBits xbits(input.x); FPBits ybits(input.y); MPFRNumber mpfrResult = binaryOperationOneOutput(op, input.x, input.y); MPFRNumber mpfrMatchValue(libcResult); OS << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n'; __llvm_libc::fputil::testing::describeValue("First input bits: ", input.x, OS); __llvm_libc::fputil::testing::describeValue("Second input bits: ", input.y, OS); OS << "Libc result: " << mpfrMatchValue.str() << '\n' << "MPFR result: " << mpfrResult.str() << '\n'; __llvm_libc::fputil::testing::describeValue( "Libc floating point result bits: ", libcResult, OS); __llvm_libc::fputil::testing::describeValue( " MPFR rounded bits: ", mpfrResult.as(), OS); OS << "ULP error: " << std::to_string(mpfrResult.ulp(libcResult)) << '\n'; } template void explainBinaryOperationOneOutputError( Operation, const BinaryInput &, float, testutils::StreamWrapper &); template void explainBinaryOperationOneOutputError( Operation, const BinaryInput &, double, testutils::StreamWrapper &); template void explainBinaryOperationOneOutputError( Operation, const BinaryInput &, long double, testutils::StreamWrapper &); template void explainTernaryOperationOneOutputError(Operation op, const TernaryInput &input, T libcResult, testutils::StreamWrapper &OS) { MPFRNumber mpfrX(input.x, Precision::value); MPFRNumber mpfrY(input.y, Precision::value); MPFRNumber mpfrZ(input.z, Precision::value); FPBits xbits(input.x); FPBits ybits(input.y); FPBits zbits(input.z); MPFRNumber mpfrResult = ternaryOperationOneOutput(op, input.x, input.y, input.z); MPFRNumber mpfrMatchValue(libcResult); OS << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << " z: " << mpfrZ.str() << '\n'; __llvm_libc::fputil::testing::describeValue("First input bits: ", input.x, OS); __llvm_libc::fputil::testing::describeValue("Second input bits: ", input.y, OS); __llvm_libc::fputil::testing::describeValue("Third input bits: ", input.z, OS); OS << "Libc result: " << mpfrMatchValue.str() << '\n' << "MPFR result: " << mpfrResult.str() << '\n'; __llvm_libc::fputil::testing::describeValue( "Libc floating point result bits: ", libcResult, OS); __llvm_libc::fputil::testing::describeValue( " MPFR rounded bits: ", mpfrResult.as(), OS); OS << "ULP error: " << std::to_string(mpfrResult.ulp(libcResult)) << '\n'; } template void explainTernaryOperationOneOutputError( Operation, const TernaryInput &, float, testutils::StreamWrapper &); template void explainTernaryOperationOneOutputError( Operation, const TernaryInput &, double, testutils::StreamWrapper &); template void explainTernaryOperationOneOutputError( Operation, const TernaryInput &, long double, testutils::StreamWrapper &); template bool compareUnaryOperationSingleOutput(Operation op, T input, T libcResult, double ulpError) { // If the ulp error is exactly 0.5 (i.e a tie), we would check that the result // is rounded to the nearest even. MPFRNumber mpfrResult = unaryOperation(op, input); double ulp = mpfrResult.ulp(libcResult); bool bitsAreEven = ((FPBits(libcResult).uintval() & 1) == 0); return (ulp < ulpError) || ((ulp == ulpError) && ((ulp != 0.5) || bitsAreEven)); } template bool compareUnaryOperationSingleOutput(Operation, float, float, double); template bool compareUnaryOperationSingleOutput(Operation, double, double, double); template bool compareUnaryOperationSingleOutput(Operation, long double, long double, double); template bool compareUnaryOperationTwoOutputs(Operation op, T input, const BinaryOutput &libcResult, double ulpError) { int mpfrIntResult; MPFRNumber mpfrResult = unaryOperationTwoOutputs(op, input, mpfrIntResult); double ulp = mpfrResult.ulp(libcResult.f); if (mpfrIntResult != libcResult.i) return false; bool bitsAreEven = ((FPBits(libcResult.f).uintval() & 1) == 0); return (ulp < ulpError) || ((ulp == ulpError) && ((ulp != 0.5) || bitsAreEven)); } template bool compareUnaryOperationTwoOutputs(Operation, float, const BinaryOutput &, double); template bool compareUnaryOperationTwoOutputs(Operation, double, const BinaryOutput &, double); template bool compareUnaryOperationTwoOutputs( Operation, long double, const BinaryOutput &, double); template bool compareBinaryOperationTwoOutputs(Operation op, const BinaryInput &input, const BinaryOutput &libcResult, double ulpError) { int mpfrIntResult; MPFRNumber mpfrResult = binaryOperationTwoOutputs(op, input.x, input.y, mpfrIntResult); double ulp = mpfrResult.ulp(libcResult.f); if (mpfrIntResult != libcResult.i) { if (op == Operation::RemQuo) { if ((0x7 & mpfrIntResult) != (0x7 & libcResult.i)) return false; } else { return false; } } bool bitsAreEven = ((FPBits(libcResult.f).uintval() & 1) == 0); return (ulp < ulpError) || ((ulp == ulpError) && ((ulp != 0.5) || bitsAreEven)); } template bool compareBinaryOperationTwoOutputs(Operation, const BinaryInput &, const BinaryOutput &, double); template bool compareBinaryOperationTwoOutputs(Operation, const BinaryInput &, const BinaryOutput &, double); template bool compareBinaryOperationTwoOutputs( Operation, const BinaryInput &, const BinaryOutput &, double); template bool compareBinaryOperationOneOutput(Operation op, const BinaryInput &input, T libcResult, double ulpError) { MPFRNumber mpfrResult = binaryOperationOneOutput(op, input.x, input.y); double ulp = mpfrResult.ulp(libcResult); bool bitsAreEven = ((FPBits(libcResult).uintval() & 1) == 0); return (ulp < ulpError) || ((ulp == ulpError) && ((ulp != 0.5) || bitsAreEven)); } template bool compareBinaryOperationOneOutput(Operation, const BinaryInput &, float, double); template bool compareBinaryOperationOneOutput(Operation, const BinaryInput &, double, double); template bool compareBinaryOperationOneOutput( Operation, const BinaryInput &, long double, double); template bool compareTernaryOperationOneOutput(Operation op, const TernaryInput &input, T libcResult, double ulpError) { MPFRNumber mpfrResult = ternaryOperationOneOutput(op, input.x, input.y, input.z); double ulp = mpfrResult.ulp(libcResult); bool bitsAreEven = ((FPBits(libcResult).uintval() & 1) == 0); return (ulp < ulpError) || ((ulp == ulpError) && ((ulp != 0.5) || bitsAreEven)); } template bool compareTernaryOperationOneOutput(Operation, const TernaryInput &, float, double); template bool compareTernaryOperationOneOutput( Operation, const TernaryInput &, double, double); template bool compareTernaryOperationOneOutput( Operation, const TernaryInput &, long double, double); static mpfr_rnd_t getMPFRRoundingMode(RoundingMode mode) { switch (mode) { case RoundingMode::Upward: return MPFR_RNDU; break; case RoundingMode::Downward: return MPFR_RNDD; break; case RoundingMode::TowardZero: return MPFR_RNDZ; break; case RoundingMode::Nearest: return MPFR_RNDN; break; } } } // namespace internal template bool RoundToLong(T x, long &result) { MPFRNumber mpfr(x); return mpfr.roundToLong(result); } template bool RoundToLong(float, long &); template bool RoundToLong(double, long &); template bool RoundToLong(long double, long &); template bool RoundToLong(T x, RoundingMode mode, long &result) { MPFRNumber mpfr(x); return mpfr.roundToLong(internal::getMPFRRoundingMode(mode), result); } template bool RoundToLong(float, RoundingMode, long &); template bool RoundToLong(double, RoundingMode, long &); template bool RoundToLong(long double, RoundingMode, long &); template T Round(T x, RoundingMode mode) { MPFRNumber mpfr(x); MPFRNumber result = mpfr.rint(internal::getMPFRRoundingMode(mode)); return result.as(); } template float Round(float, RoundingMode); template double Round(double, RoundingMode); template long double Round(long double, RoundingMode); } // namespace mpfr } // namespace testing } // namespace __llvm_libc