forked from OSchip/llvm-project
[libc] Improve information printed on failure of a math test which uses MPFR.
A new test matcher class MPFRMatcher is added along with helper macros EXPECT|ASSERT_MPFR_MATCH. New type traits classes RemoveCV and IsFloatingPointType have been added and used to implement the above class and its helpers. Reviewers: abrachet, phosek Differential Revision: https://reviews.llvm.org/D79256
This commit is contained in:
parent
7cbbf89d23
commit
d69cbd826a
|
@ -76,7 +76,7 @@ TEST(CosfTest, InFloatRange) {
|
|||
float x = as_float(v);
|
||||
if (isnan(x) || isinf(x))
|
||||
continue;
|
||||
EXPECT_TRUE(mpfr::equalsCos(x, __llvm_libc::cosf(x), tolerance));
|
||||
ASSERT_MPFR_MATCH(mpfr::OP_Cos, x, __llvm_libc::cosf(x), tolerance);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,12 +84,12 @@ TEST(CosfTest, InFloatRange) {
|
|||
TEST(CosfTest, SmallValues) {
|
||||
float x = as_float(0x17800000);
|
||||
float result = __llvm_libc::cosf(x);
|
||||
EXPECT_TRUE(mpfr::equalsCos(x, result, tolerance));
|
||||
EXPECT_MPFR_MATCH(mpfr::OP_Cos, x, result, tolerance);
|
||||
EXPECT_EQ(FloatBits::One, as_uint32_bits(result));
|
||||
|
||||
x = as_float(0x00400000);
|
||||
x = as_float(0x0040000);
|
||||
result = __llvm_libc::cosf(x);
|
||||
EXPECT_TRUE(mpfr::equalsCos(x, result, tolerance));
|
||||
EXPECT_MPFR_MATCH(mpfr::OP_Cos, x, result, tolerance);
|
||||
EXPECT_EQ(FloatBits::One, as_uint32_bits(result));
|
||||
}
|
||||
|
||||
|
@ -98,6 +98,6 @@ TEST(CosfTest, SmallValues) {
|
|||
TEST(CosfTest, SDCOMP_26094) {
|
||||
for (uint32_t v : sdcomp26094Values) {
|
||||
float x = as_float(v);
|
||||
EXPECT_TRUE(mpfr::equalsCos(x, __llvm_libc::cosf(x), tolerance));
|
||||
ASSERT_MPFR_MATCH(mpfr::OP_Cos, x, __llvm_libc::cosf(x), tolerance);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,8 +87,8 @@ TEST(SinCosfTest, InFloatRange) {
|
|||
|
||||
float sin, cos;
|
||||
__llvm_libc::sincosf(x, &sin, &cos);
|
||||
EXPECT_TRUE(mpfr::equalsCos(x, cos, tolerance));
|
||||
EXPECT_TRUE(mpfr::equalsSin(x, sin, tolerance));
|
||||
ASSERT_MPFR_MATCH(mpfr::OP_Cos, x, cos, tolerance);
|
||||
ASSERT_MPFR_MATCH(mpfr::OP_Sin, x, sin, tolerance);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,16 +98,16 @@ TEST(SinCosfTest, SmallValues) {
|
|||
float x = as_float(bits);
|
||||
float result_cos, result_sin;
|
||||
__llvm_libc::sincosf(x, &result_sin, &result_cos);
|
||||
EXPECT_TRUE(mpfr::equalsCos(x, result_cos, tolerance));
|
||||
EXPECT_TRUE(mpfr::equalsSin(x, result_sin, tolerance));
|
||||
EXPECT_MPFR_MATCH(mpfr::OP_Cos, x, result_cos, tolerance);
|
||||
EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, result_sin, tolerance);
|
||||
EXPECT_EQ(FloatBits::One, as_uint32_bits(result_cos));
|
||||
EXPECT_EQ(bits, as_uint32_bits(result_sin));
|
||||
|
||||
bits = 0x00400000;
|
||||
x = as_float(bits);
|
||||
__llvm_libc::sincosf(x, &result_sin, &result_cos);
|
||||
EXPECT_TRUE(mpfr::equalsCos(x, result_cos, tolerance));
|
||||
EXPECT_TRUE(mpfr::equalsSin(x, result_sin, tolerance));
|
||||
EXPECT_MPFR_MATCH(mpfr::OP_Cos, x, result_cos, tolerance);
|
||||
EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, result_sin, tolerance);
|
||||
EXPECT_EQ(FloatBits::One, as_uint32_bits(result_cos));
|
||||
EXPECT_EQ(bits, as_uint32_bits(result_sin));
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ TEST(SinCosfTest, SDCOMP_26094) {
|
|||
float x = as_float(v);
|
||||
float sin, cos;
|
||||
__llvm_libc::sincosf(x, &sin, &cos);
|
||||
EXPECT_TRUE(mpfr::equalsCos(x, cos, tolerance));
|
||||
EXPECT_TRUE(mpfr::equalsSin(x, sin, tolerance));
|
||||
EXPECT_MPFR_MATCH(mpfr::OP_Cos, x, cos, tolerance);
|
||||
EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, sin, tolerance);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,13 +76,13 @@ TEST(SinfTest, InFloatRange) {
|
|||
float x = as_float(v);
|
||||
if (isnan(x) || isinf(x))
|
||||
continue;
|
||||
EXPECT_TRUE(mpfr::equalsSin(x, __llvm_libc::sinf(x), tolerance));
|
||||
ASSERT_MPFR_MATCH(mpfr::OP_Sin, x, __llvm_libc::sinf(x), tolerance);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SinfTest, SpecificBitPatterns) {
|
||||
float x = as_float(0xc70d39a1);
|
||||
EXPECT_TRUE(mpfr::equalsSin(x, __llvm_libc::sinf(x), tolerance));
|
||||
EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, __llvm_libc::sinf(x), tolerance);
|
||||
}
|
||||
|
||||
// For small values, sin(x) is x.
|
||||
|
@ -90,13 +90,13 @@ TEST(SinfTest, SmallValues) {
|
|||
uint32_t bits = 0x17800000;
|
||||
float x = as_float(bits);
|
||||
float result = __llvm_libc::sinf(x);
|
||||
EXPECT_TRUE(mpfr::equalsSin(x, result, tolerance));
|
||||
EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, result, tolerance);
|
||||
EXPECT_EQ(bits, as_uint32_bits(result));
|
||||
|
||||
bits = 0x00400000;
|
||||
x = as_float(bits);
|
||||
result = __llvm_libc::sinf(x);
|
||||
EXPECT_TRUE(mpfr::equalsSin(x, result, tolerance));
|
||||
EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, result, tolerance);
|
||||
EXPECT_EQ(bits, as_uint32_bits(result));
|
||||
}
|
||||
|
||||
|
@ -105,6 +105,6 @@ TEST(SinfTest, SmallValues) {
|
|||
TEST(SinfTest, SDCOMP_26094) {
|
||||
for (uint32_t v : sdcomp26094Values) {
|
||||
float x = as_float(v);
|
||||
EXPECT_TRUE(mpfr::equalsSin(x, __llvm_libc::sinf(x), tolerance));
|
||||
EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, __llvm_libc::sinf(x), tolerance);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,22 @@ template <typename T> struct IsPointerType<T *> : public TrueValue {};
|
|||
template <typename T1, typename T2> struct IsSame : public FalseValue {};
|
||||
template <typename T> struct IsSame<T, T> : public TrueValue {};
|
||||
|
||||
template <typename T> struct TypeIdentity { typedef T Type; };
|
||||
|
||||
template <typename T> struct RemoveCV : public TypeIdentity<T> {};
|
||||
template <typename T> struct RemoveCV<const T> : public TypeIdentity<T> {};
|
||||
template <typename T> struct RemoveCV<volatile T> : public TypeIdentity<T> {};
|
||||
template <typename T>
|
||||
struct RemoveCV<const volatile T> : public TypeIdentity<T> {};
|
||||
|
||||
template <typename T> using RemoveCVType = typename RemoveCV<T>::Type;
|
||||
|
||||
template <typename Type> struct IsFloatingPointType {
|
||||
static constexpr bool Value = IsSame<float, RemoveCVType<Type>>::Value ||
|
||||
IsSame<double, RemoveCVType<Type>>::Value ||
|
||||
IsSame<long double, RemoveCVType<Type>>::Value;
|
||||
};
|
||||
|
||||
} // namespace cpp
|
||||
} // namespace __llvm_libc
|
||||
|
||||
|
|
|
@ -12,7 +12,8 @@ if(LIBC_TESTS_CAN_USE_MPFR)
|
|||
MPFRUtils.cpp
|
||||
MPFRUtils.h
|
||||
)
|
||||
target_link_libraries(libcMPFRWrapper -lmpfr -lgmp)
|
||||
add_dependencies(libcMPFRWrapper libc.utils.CPP.standalone_cpp LibcUnitTest LLVMSupport)
|
||||
target_link_libraries(libcMPFRWrapper -lmpfr -lgmp LibcUnitTest LLVMSupport)
|
||||
else()
|
||||
message(WARNING "Math tests using MPFR will be skipped.")
|
||||
endif()
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
|
||||
#include "MPFRUtils.h"
|
||||
|
||||
#include <iostream>
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
#include <mpfr.h>
|
||||
#include <string>
|
||||
|
||||
namespace __llvm_libc {
|
||||
namespace testing {
|
||||
|
@ -25,11 +27,38 @@ class MPFRNumber {
|
|||
public:
|
||||
MPFRNumber() { mpfr_init2(value, mpfrPrecision); }
|
||||
|
||||
explicit MPFRNumber(float x) {
|
||||
// We use explicit EnableIf specializations to disallow implicit
|
||||
// conversions. Implicit conversions can potentially lead to loss of
|
||||
// precision.
|
||||
template <typename XType,
|
||||
cpp::EnableIfType<cpp::IsSame<float, XType>::Value, int> = 0>
|
||||
explicit MPFRNumber(XType x) {
|
||||
mpfr_init2(value, mpfrPrecision);
|
||||
mpfr_set_flt(value, x, MPFR_RNDN);
|
||||
}
|
||||
|
||||
template <typename XType,
|
||||
cpp::EnableIfType<cpp::IsSame<double, XType>::Value, int> = 0>
|
||||
explicit MPFRNumber(XType x) {
|
||||
mpfr_init2(value, mpfrPrecision);
|
||||
mpfr_set_d(value, x, MPFR_RNDN);
|
||||
}
|
||||
|
||||
template <typename XType,
|
||||
cpp::EnableIfType<cpp::IsFloatingPointType<XType>::Value, int> = 0>
|
||||
MPFRNumber(Operation op, XType rawValue) {
|
||||
mpfr_init2(value, mpfrPrecision);
|
||||
MPFRNumber mpfrInput(rawValue);
|
||||
switch (op) {
|
||||
case OP_Cos:
|
||||
mpfr_cos(value, mpfrInput.value, MPFR_RNDN);
|
||||
break;
|
||||
case OP_Sin:
|
||||
mpfr_sin(value, mpfrInput.value, MPFR_RNDN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MPFRNumber(const MPFRNumber &other) {
|
||||
mpfr_set(value, other.value, MPFR_RNDN);
|
||||
}
|
||||
|
@ -59,38 +88,51 @@ public:
|
|||
return mpfr_lessequal_p(difference.value, tolerance.value);
|
||||
}
|
||||
|
||||
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);
|
||||
llvm::StringRef ref(buffer);
|
||||
ref = ref.trim();
|
||||
return ref.str();
|
||||
}
|
||||
|
||||
// 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); }
|
||||
void dump(const char *msg) const { mpfr_printf("%s%.128Rf\n", msg, value); }
|
||||
|
||||
public:
|
||||
static MPFRNumber cos(float x) {
|
||||
MPFRNumber result;
|
||||
MPFRNumber mpfrX(x);
|
||||
mpfr_cos(result.value, mpfrX.value, MPFR_RNDN);
|
||||
return result;
|
||||
}
|
||||
|
||||
static MPFRNumber sin(float x) {
|
||||
MPFRNumber result;
|
||||
MPFRNumber mpfrX(x);
|
||||
mpfr_sin(result.value, mpfrX.value, MPFR_RNDN);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
bool equalsCos(float input, float libcOutput, const Tolerance &t) {
|
||||
MPFRNumber mpfrResult = MPFRNumber::cos(input);
|
||||
MPFRNumber libcResult(libcOutput);
|
||||
return mpfrResult.isEqual(libcResult, t);
|
||||
namespace internal {
|
||||
|
||||
template <typename T>
|
||||
void MPFRMatcher<T>::explainError(testutils::StreamWrapper &OS) {
|
||||
MPFRNumber mpfrResult(operation, input);
|
||||
MPFRNumber mpfrInput(input);
|
||||
MPFRNumber mpfrMatchValue(matchValue);
|
||||
OS << "Match value not within tolerance value of MPFR result:\n"
|
||||
<< "Operation input: " << mpfrInput.str() << '\n'
|
||||
<< " Match value: " << mpfrMatchValue.str() << '\n'
|
||||
<< " MPFR result: " << mpfrResult.str() << '\n';
|
||||
}
|
||||
|
||||
bool equalsSin(float input, float libcOutput, const Tolerance &t) {
|
||||
MPFRNumber mpfrResult = MPFRNumber::sin(input);
|
||||
MPFRNumber libcResult(libcOutput);
|
||||
return mpfrResult.isEqual(libcResult, t);
|
||||
}
|
||||
template void MPFRMatcher<float>::explainError(testutils::StreamWrapper &);
|
||||
template void MPFRMatcher<double>::explainError(testutils::StreamWrapper &);
|
||||
|
||||
template <typename T>
|
||||
bool compare(Operation op, T input, T libcResult, const Tolerance &t) {
|
||||
MPFRNumber mpfrResult(op, input);
|
||||
MPFRNumber mpfrInput(input);
|
||||
MPFRNumber mpfrLibcResult(libcResult);
|
||||
return mpfrResult.isEqual(mpfrLibcResult, t);
|
||||
};
|
||||
|
||||
template bool compare<float>(Operation, float, float, const Tolerance &);
|
||||
template bool compare<double>(Operation, double, double, const Tolerance &);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace mpfr
|
||||
} // namespace testing
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
#ifndef LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H
|
||||
#define LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H
|
||||
|
||||
#include "utils/CPP/TypeTraits.h"
|
||||
#include "utils/UnitTest/Test.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace __llvm_libc {
|
||||
|
@ -36,16 +39,57 @@ struct Tolerance {
|
|||
uint32_t bits;
|
||||
};
|
||||
|
||||
// Return true if |libcOutput| is within the tolerance |t| of the cos(x)
|
||||
// value as evaluated by MPFR.
|
||||
bool equalsCos(float x, float libcOutput, const Tolerance &t);
|
||||
enum Operation {
|
||||
OP_Cos,
|
||||
OP_Sin,
|
||||
};
|
||||
|
||||
// Return true if |libcOutput| is within the tolerance |t| of the sin(x)
|
||||
// value as evaluated by MPFR.
|
||||
bool equalsSin(float x, float libcOutput, const Tolerance &t);
|
||||
namespace internal {
|
||||
|
||||
template <typename T>
|
||||
bool compare(Operation op, T input, T libcOutput, const Tolerance &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.");
|
||||
|
||||
Operation operation;
|
||||
T input;
|
||||
Tolerance tolerance;
|
||||
T matchValue;
|
||||
|
||||
public:
|
||||
MPFRMatcher(Operation op, T testInput, Tolerance &t)
|
||||
: operation(op), input(testInput), tolerance(t) {}
|
||||
|
||||
bool match(T libcResult) {
|
||||
matchValue = libcResult;
|
||||
return internal::compare(operation, input, libcResult, tolerance);
|
||||
}
|
||||
|
||||
void explainError(testutils::StreamWrapper &OS) override;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename T>
|
||||
internal::MPFRMatcher<T> getMPFRMatcher(Operation op, T input, Tolerance t) {
|
||||
static_assert(
|
||||
__llvm_libc::cpp::IsFloatingPointType<T>::Value,
|
||||
"getMPFRMatcher can only be used to match floating point results.");
|
||||
return internal::MPFRMatcher<T>(op, input, t);
|
||||
}
|
||||
|
||||
} // namespace mpfr
|
||||
} // namespace testing
|
||||
} // namespace __llvm_libc
|
||||
|
||||
#define EXPECT_MPFR_MATCH(op, input, matchValue, tolerance) \
|
||||
EXPECT_THAT(matchValue, __llvm_libc::testing::mpfr::getMPFRMatcher( \
|
||||
op, input, tolerance))
|
||||
|
||||
#define ASSERT_MPFR_MATCH(op, input, matchValue, tolerance) \
|
||||
ASSERT_THAT(matchValue, __llvm_libc::testing::mpfr::getMPFRMatcher( \
|
||||
op, input, tolerance))
|
||||
|
||||
#endif // LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace __llvm_libc {
|
||||
namespace testutils {
|
||||
|
@ -41,6 +42,7 @@ template StreamWrapper &
|
|||
template StreamWrapper &
|
||||
StreamWrapper::operator<<<unsigned long long>(unsigned long long t);
|
||||
template StreamWrapper &StreamWrapper::operator<<<bool>(bool t);
|
||||
template StreamWrapper &StreamWrapper::operator<<<std::string>(std::string t);
|
||||
|
||||
} // namespace testutils
|
||||
} // namespace __llvm_libc
|
||||
|
|
Loading…
Reference in New Issue