[libc-commits] [libc] 9887a70 - [libc] Add ULP function to MPFRNumber class to test correctly rounded functions such as SQRT, FMA.
Tue Ly via libc-commits
libc-commits at lists.llvm.org
Tue Aug 18 10:52:37 PDT 2020
Author: Tue Ly
Date: 2020-08-18T13:51:58-04:00
New Revision: 9887a70e7a768f6fca135587ce3e62d691a3646d
URL: https://github.com/llvm/llvm-project/commit/9887a70e7a768f6fca135587ce3e62d691a3646d
DIFF: https://github.com/llvm/llvm-project/commit/9887a70e7a768f6fca135587ce3e62d691a3646d.diff
LOG: [libc] Add ULP function to MPFRNumber class to test correctly rounded functions such as SQRT, FMA.
Add ULP function to MPFRNumber class to test correctly rounded functions.
Differential Revision: https://reviews.llvm.org/D84725
Added:
Modified:
libc/utils/MPFRWrapper/MPFRUtils.cpp
libc/utils/MPFRWrapper/MPFRUtils.h
Removed:
################################################################################
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index c6020f471e88..c97e89ce9b2b 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -9,6 +9,7 @@
#include "MPFRUtils.h"
#include "utils/FPUtil/FPBits.h"
+#include "utils/FPUtil/TestHelpers.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
@@ -119,6 +120,9 @@ class MPFRNumber {
case Operation::Sin:
mpfr_sin(value, mpfrInput.value, MPFR_RNDN);
break;
+ case Operation::Sqrt:
+ mpfr_sqrt(value, mpfrInput.value, MPFR_RNDN);
+ break;
case Operation::Trunc:
mpfr_trunc(value, mpfrInput.value);
break;
@@ -155,9 +159,59 @@ class MPFRNumber {
}
// These functions are useful for debugging.
- float asFloat() const { return mpfr_get_flt(value, MPFR_RNDN); }
- double asDouble() const { return mpfr_get_d(value, MPFR_RNDN); }
+ template <typename T> T as() const;
+
+ template <> float as<float>() const { return mpfr_get_flt(value, MPFR_RNDN); }
+ template <> double as<double>() const { return mpfr_get_d(value, MPFR_RNDN); }
+ template <> long double as<long double>() 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)
diff erence between the
+ // stored MPFR and a floating point number.
+ //
+ // We define:
+ // ULP(mpfr_value, value) = abs(mpfr_value - value) / eps(value)
+ //
+ // Remarks:
+ // 1. ULP < 0.5 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.
+ template <typename T>
+ cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, double> ulp(T input) {
+ fputil::FPBits<T> bits(input);
+ MPFRNumber mpfrInput(input);
+
+ // abs(value - input)
+ mpfr_sub(mpfrInput.value, value, mpfrInput.value, MPFR_RNDN);
+ mpfr_abs(mpfrInput.value, mpfrInput.value, MPFR_RNDN);
+
+ // get eps(input)
+ int epsExponent = bits.exponent - fputil::FPBits<T>::exponentBias -
+ fputil::MantissaWidth<T>::value;
+ if (bits.exponent == 0) {
+ // correcting denormal exponent
+ ++epsExponent;
+ } else if ((bits.mantissa == 0) && (bits.exponent > 1) &&
+ mpfr_less_p(value, mpfrInput.value)) {
+ // when the input is exactly 2^n, distance (epsilon) between the input
+ // and the next floating point number is
diff erent from the distance to
+ // the previous floating point number. So in that case, if the correct
+ // value from MPFR is smaller than the input, we use the smaller epsilon
+ --epsExponent;
+ }
+
+ // Since eps(value) is of the form 2^e, instead of dividing such number,
+ // we multiply by its inverse 2^{-e}.
+ mpfr_mul_2si(mpfrInput.value, mpfrInput.value, -epsExponent, MPFR_RNDN);
+
+ return mpfrInput.as<double>();
+ }
};
namespace internal {
@@ -167,19 +221,26 @@ void MPFRMatcher<T>::explainError(testutils::StreamWrapper &OS) {
MPFRNumber mpfrResult(operation, input);
MPFRNumber mpfrInput(input);
MPFRNumber mpfrMatchValue(matchValue);
- MPFRNumber mpfrToleranceValue(matchValue, tolerance);
FPBits<T> inputBits(input);
FPBits<T> matchBits(matchValue);
- // TODO: Call to llvm::utohexstr implicitly converts __uint128_t values to
- // uint64_t values. This can be fixed using a custom wrapper for
- // llvm::utohexstr to handle __uint128_t values correctly.
+ FPBits<T> mpfrResultBits(mpfrResult.as<T>());
OS << "Match value not within tolerance value of MPFR result:\n"
- << " Input decimal: " << mpfrInput.str() << '\n'
- << " Input bits: 0x" << llvm::utohexstr(inputBits.bitsAsUInt()) << '\n'
- << " Match decimal: " << mpfrMatchValue.str() << '\n'
- << " Match bits: 0x" << llvm::utohexstr(matchBits.bitsAsUInt()) << '\n'
- << " MPFR result: " << mpfrResult.str() << '\n'
- << "Tolerance value: " << mpfrToleranceValue.str() << '\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<T>(), OS);
+ OS << '\n';
+ if (useULP) {
+ OS << " ULP error: " << std::to_string(mpfrResult.ulp(matchValue))
+ << '\n';
+ } else {
+ MPFRNumber mpfrToleranceValue = MPFRNumber(matchValue, tolerance);
+ OS << "Tolerance value: " << mpfrToleranceValue.str() << '\n';
+ }
}
template void MPFRMatcher<float>::explainError(testutils::StreamWrapper &);
@@ -201,6 +262,21 @@ template bool compare<double>(Operation, double, double, const Tolerance &);
template bool compare<long double>(Operation, long double, long double,
const Tolerance &);
+template <typename T>
+bool compare(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(op, input);
+ double ulp = mpfrResult.ulp(libcResult);
+ bool bitsAreEven = ((FPBits<T>(libcResult).bitsAsUInt() & 1) == 0);
+ return (ulp < ulpError) ||
+ ((ulp == ulpError) && ((ulp != 0.5) || bitsAreEven));
+}
+
+template bool compare<float>(Operation, float, float, double);
+template bool compare<double>(Operation, double, double, double);
+template bool compare<long double>(Operation, long double, long double, double);
+
} // namespace internal
} // namespace mpfr
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.h b/libc/utils/MPFRWrapper/MPFRUtils.h
index 562816565332..633c67ff8570 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.h
+++ b/libc/utils/MPFRWrapper/MPFRUtils.h
@@ -48,6 +48,7 @@ enum class Operation : int {
Floor,
Round,
Sin,
+ Sqrt,
Trunc
};
@@ -56,6 +57,9 @@ namespace internal {
template <typename T>
bool compare(Operation op, T input, T libcOutput, const Tolerance &t);
+template <typename T>
+bool compare(Operation op, T input, T libcOutput, double t);
+
template <typename T> class MPFRMatcher : public testing::Matcher<T> {
static_assert(__llvm_libc::cpp::IsFloatingPointType<T>::Value,
"MPFRMatcher can only be used with floating point values.");
@@ -64,14 +68,21 @@ template <typename T> class MPFRMatcher : public testing::Matcher<T> {
T input;
Tolerance tolerance;
T matchValue;
+ double ulpTolerance;
+ bool useULP;
public:
MPFRMatcher(Operation op, T testInput, Tolerance &t)
- : operation(op), input(testInput), tolerance(t) {}
+ : operation(op), input(testInput), tolerance(t), useULP(false) {}
+ MPFRMatcher(Operation op, T testInput, double ulpTolerance)
+ : operation(op), input(testInput), ulpTolerance(ulpTolerance),
+ useULP(true) {}
bool match(T libcResult) {
matchValue = libcResult;
- return internal::compare(operation, input, libcResult, tolerance);
+ return (useULP
+ ? internal::compare(operation, input, libcResult, ulpTolerance)
+ : internal::compare(operation, input, libcResult, tolerance));
}
void explainError(testutils::StreamWrapper &OS) override;
@@ -79,9 +90,12 @@ template <typename T> class MPFRMatcher : public testing::Matcher<T> {
} // namespace internal
-template <typename T>
+template <typename T, typename U>
__attribute__((no_sanitize("address")))
-internal::MPFRMatcher<T> getMPFRMatcher(Operation op, T input, Tolerance t) {
+typename cpp::EnableIfType<cpp::IsSameV<U, Tolerance> ||
+ cpp::IsSameV<U, double>,
+ internal::MPFRMatcher<T>>
+getMPFRMatcher(Operation op, T input, U t) {
static_assert(
__llvm_libc::cpp::IsFloatingPointType<T>::Value,
"getMPFRMatcher can only be used to match floating point results.");
More information about the libc-commits
mailing list