[libc-commits] [libc] dba74c6 - [libc] Make ULP error reflect the bit distance more closely.
Siva Chandra Reddy via libc-commits
libc-commits at lists.llvm.org
Fri Jul 2 09:56:22 PDT 2021
Author: Siva Chandra Reddy
Date: 2021-07-02T16:56:01Z
New Revision: dba74c68178bfaa54e6270d4790b78ef5b6e37c2
URL: https://github.com/llvm/llvm-project/commit/dba74c68178bfaa54e6270d4790b78ef5b6e37c2
DIFF: https://github.com/llvm/llvm-project/commit/dba74c68178bfaa54e6270d4790b78ef5b6e37c2.diff
LOG: [libc] Make ULP error reflect the bit distance more closely.
Reviewed By: lntue
Differential Revision: https://reviews.llvm.org/D105334
Added:
Modified:
libc/utils/MPFRWrapper/MPFRUtils.cpp
Removed:
################################################################################
diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp
index 959701cf94125..a61f02243579c 100644
--- a/libc/utils/MPFRWrapper/MPFRUtils.cpp
+++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp
@@ -12,6 +12,7 @@
#include "utils/FPUtil/FPBits.h"
#include "utils/FPUtil/TestHelpers.h"
+#include <cmath>
#include <memory>
#include <stdint.h>
#include <string>
@@ -256,51 +257,64 @@ class MPFRNumber {
// 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)
+ // We define ULP
diff erence 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. ULP < 0.5 will imply that the value is correctly rounded.
+ // 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
diff erence 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 <typename T>
cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, double> ulp(T input) {
- fputil::FPBits<T> bits(input);
- MPFRNumber mpfrInput(input);
- // When calculating error, we first round this number to the floating
- // point type T and calculate the ulp error against this rounded number.
- // The input is always either exact or rounded. So, we if compare
- // with this number directly, we can end up with a large ULP error.
- MPFRNumber thisRoundedToFloat(as<T>());
-
- // abs(thisRoundedToFloat - input)
- mpfr_sub(mpfrInput.value, thisRoundedToFloat.value, mpfrInput.value,
- MPFR_RNDN);
- mpfr_abs(mpfrInput.value, mpfrInput.value, MPFR_RNDN);
-
- // get eps(input)
- int epsExponent = bits.encoding.exponent - fputil::FPBits<T>::exponentBias -
- fputil::MantissaWidth<T>::value;
- if (bits.encoding.exponent == 0) {
- // correcting denormal exponent
- ++epsExponent;
- } else if ((bits.encoding.mantissa == 0) && (bits.encoding.exponent > 1) &&
- mpfr_less_p(thisRoundedToFloat.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;
+ T thisAsT = as<T>();
+ int thisExponent = fputil::FPBits<T>(thisAsT).getExponent();
+ int inputExponent = fputil::FPBits<T>(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<double>();
}
- // 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);
+ // If the control reaches here, it means that this number and input are
+ // of the same sign but
diff erent 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<T>(min).getExponent();
+ int maxExponent = fputil::FPBits<T>(max).getExponent();
- return mpfrInput.as<double>();
+ 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<double>();
}
};
More information about the libc-commits
mailing list