diff --git a/libc/test/src/math/cosf_test.cpp b/libc/test/src/math/cosf_test.cpp --- a/libc/test/src/math/cosf_test.cpp +++ b/libc/test/src/math/cosf_test.cpp @@ -69,35 +69,35 @@ EXPECT_EQ(llvmlibc_errno, EDOM); } -TEST(CosfTest, InFloatRange) { +MPFR_TEST(CosfTest, InFloatRange) { constexpr uint32_t count = 1000000; constexpr uint32_t step = UINT32_MAX / count; for (uint32_t i = 0, v = 0; i <= count; ++i, v += step) { float x = as_float(v); if (isnan(x) || isinf(x)) continue; - EXPECT_TRUE(mpfr::equalsCos(x, __llvm_libc::cosf(x), tolerance)); + ASSERT_MPFR_EQ(mpfr::OP_Cos, x, __llvm_libc::cosf(x), tolerance); } } // For small values, cos(x) is 1. -TEST(CosfTest, SmallValues) { +MPFR_TEST(CosfTest, SmallValues) { float x = as_float(0x17800000); float result = __llvm_libc::cosf(x); - EXPECT_TRUE(mpfr::equalsCos(x, result, tolerance)); + EXPECT_MPFR_EQ(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_EQ(mpfr::OP_Cos, x, result, tolerance); EXPECT_EQ(FloatBits::One, as_uint32_bits(result)); } // SDCOMP-26094: check cosf in the cases for which the range reducer // returns values furthest beyond its nominal upper bound of pi/4. -TEST(CosfTest, SDCOMP_26094) { +MPFR_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_EQ(mpfr::OP_Cos, x, __llvm_libc::cosf(x), tolerance); } } diff --git a/libc/test/src/math/sincosf_test.cpp b/libc/test/src/math/sincosf_test.cpp --- a/libc/test/src/math/sincosf_test.cpp +++ b/libc/test/src/math/sincosf_test.cpp @@ -77,7 +77,7 @@ EXPECT_EQ(llvmlibc_errno, EDOM); } -TEST(SinCosfTest, InFloatRange) { +MPFR_TEST(SinCosfTest, InFloatRange) { constexpr uint32_t count = 1000000; constexpr uint32_t step = UINT32_MAX / count; for (uint32_t i = 0, v = 0; i <= count; ++i, v += step) { @@ -87,39 +87,39 @@ 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_EQ(mpfr::OP_Cos, x, cos, tolerance); + ASSERT_MPFR_EQ(mpfr::OP_Sin, x, sin, tolerance); } } // For small values, cos(x) is 1 and sin(x) is x. -TEST(SinCosfTest, SmallValues) { +MPFR_TEST(SinCosfTest, SmallValues) { uint32_t bits = 0x17800000; 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_EQ(mpfr::OP_Cos, x, result_cos, tolerance); + EXPECT_MPFR_EQ(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_EQ(mpfr::OP_Cos, x, result_cos, tolerance); + EXPECT_MPFR_EQ(mpfr::OP_Sin, x, result_sin, tolerance); EXPECT_EQ(FloatBits::One, as_uint32_bits(result_cos)); EXPECT_EQ(bits, as_uint32_bits(result_sin)); } // SDCOMP-26094: check sinf in the cases for which the range reducer // returns values furthest beyond its nominal upper bound of pi/4. -TEST(SinCosfTest, SDCOMP_26094) { +MPFR_TEST(SinCosfTest, SDCOMP_26094) { for (uint32_t v : sdcomp26094Values) { 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_EQ(mpfr::OP_Cos, x, cos, tolerance); + EXPECT_MPFR_EQ(mpfr::OP_Sin, x, sin, tolerance); } } diff --git a/libc/test/src/math/sinf_test.cpp b/libc/test/src/math/sinf_test.cpp --- a/libc/test/src/math/sinf_test.cpp +++ b/libc/test/src/math/sinf_test.cpp @@ -69,42 +69,42 @@ EXPECT_EQ(llvmlibc_errno, EDOM); } -TEST(SinfTest, InFloatRange) { +MPFR_TEST(SinfTest, InFloatRange) { constexpr uint32_t count = 1000000; constexpr uint32_t step = UINT32_MAX / count; for (uint32_t i = 0, v = 0; i <= count; ++i, v += step) { float x = as_float(v); if (isnan(x) || isinf(x)) continue; - EXPECT_TRUE(mpfr::equalsSin(x, __llvm_libc::sinf(x), tolerance)); + ASSERT_MPFR_EQ(mpfr::OP_Sin, x, __llvm_libc::sinf(x), tolerance); } } -TEST(SinfTest, SpecificBitPatterns) { +MPFR_TEST(SinfTest, SpecificBitPatterns) { float x = as_float(0xc70d39a1); - EXPECT_TRUE(mpfr::equalsSin(x, __llvm_libc::sinf(x), tolerance)); + EXPECT_MPFR_EQ(mpfr::OP_Sin, x, __llvm_libc::sinf(x), tolerance); } // For small values, sin(x) is x. -TEST(SinfTest, SmallValues) { +MPFR_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_EQ(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_EQ(mpfr::OP_Sin, x, result, tolerance); EXPECT_EQ(bits, as_uint32_bits(result)); } // SDCOMP-26094: check sinf in the cases for which the range reducer // returns values furthest beyond its nominal upper bound of pi/4. -TEST(SinfTest, SDCOMP_26094) { +MPFR_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_EQ(mpfr::OP_Sin, x, __llvm_libc::sinf(x), tolerance); } } diff --git a/libc/utils/CPP/TypeTraits.h b/libc/utils/CPP/TypeTraits.h --- a/libc/utils/CPP/TypeTraits.h +++ b/libc/utils/CPP/TypeTraits.h @@ -46,6 +46,19 @@ template struct IsSame : public FalseValue {}; template struct IsSame : public TrueValue {}; +template struct RemoveCV { typedef T Type; }; +template struct RemoveCV { typedef T type; }; +template struct RemoveCV { typedef T type; }; +template struct RemoveCV { typedef T type; }; + +template using RemoveCVType = typename RemoveCV::Type; + +template struct IsFloatingPointType { + static constexpr bool Value = IsSame>::Value || + IsSame>::Value || + IsSame>::Value; +}; + } // namespace cpp } // namespace __llvm_libc diff --git a/libc/utils/MPFRWrapper/CMakeLists.txt b/libc/utils/MPFRWrapper/CMakeLists.txt --- a/libc/utils/MPFRWrapper/CMakeLists.txt +++ b/libc/utils/MPFRWrapper/CMakeLists.txt @@ -12,7 +12,8 @@ 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() diff --git a/libc/utils/MPFRWrapper/MPFRUtils.h b/libc/utils/MPFRWrapper/MPFRUtils.h --- a/libc/utils/MPFRWrapper/MPFRUtils.h +++ b/libc/utils/MPFRWrapper/MPFRUtils.h @@ -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 namespace __llvm_libc { @@ -36,16 +39,44 @@ 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, +}; + +namespace internal { -// 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); +template +bool compare(Operation op, T input, T libcOutput, const Tolerance &t); + +} // namespace internal + +class Test : public __llvm_libc::testing::Test { +protected: + template ::Value, int> = 0> + static bool compare(__llvm_libc::testing::mpfr::Operation op, T input, + T libcOutput, + const __llvm_libc::testing::mpfr::Tolerance &t) { + return internal::compare(op, input, libcOutput, t); + } +}; } // namespace mpfr } // namespace testing } // namespace __llvm_libc +#define MPFR_TEST(SuiteName, TestName) \ + TEST_WITH_BASE_CLASS(SuiteName, TestName, __llvm_libc::testing::mpfr::Test) + +#define EXPECT_MPFR_EQ(op, input, libcOutput, tolerance) \ + TEST_EXTENSION(__llvm_libc::testing::mpfr::Test::compare( \ + op, input, libcOutput, tolerance), \ + true) + +#define ASSERT_MPFR_EQ(op, input, libcOutput, tolerance) \ + if (!EXPECT_MPFR_EQ(op, input, libcOutput, tolerance)) \ + return + #endif // LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp --- a/libc/utils/MPFRWrapper/MPFRUtils.cpp +++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp @@ -8,7 +8,8 @@ #include "MPFRUtils.h" -#include +#include "llvm/Support/raw_ostream.h" + #include namespace __llvm_libc { @@ -22,14 +23,26 @@ mpfr_t value; + template + friend bool internal::compare(Operation, T, T, const Tolerance &); + public: MPFRNumber() { mpfr_init2(value, mpfrPrecision); } - explicit MPFRNumber(float x) { + template ::Value, int> = 0> + explicit MPFRNumber(XType x) { mpfr_init2(value, mpfrPrecision); mpfr_set_flt(value, x, MPFR_RNDN); } + template ::Value, int> = 0> + explicit MPFRNumber(XType x) { + mpfr_init2(value, mpfrPrecision); + mpfr_set_d(value, x, MPFR_RNDN); + } + MPFRNumber(const MPFRNumber &other) { mpfr_set(value, other.value, MPFR_RNDN); } @@ -59,38 +72,58 @@ 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. + char buffer[200]; + mpfr_sprintf(buffer, "%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; +namespace internal { + +template +bool compare(Operation op, T input, T libcResult, const Tolerance &t) { + MPFRNumber mpfrResult; + MPFRNumber mpfrInput(input); + MPFRNumber mpfrLibcResult(libcResult); + + switch (op) { + case OP_Cos: + mpfr_cos(mpfrResult.value, mpfrInput.value, MPFR_RNDN); + break; + case OP_Sin: + mpfr_sin(mpfrResult.value, mpfrInput.value, MPFR_RNDN); + break; } - static MPFRNumber sin(float x) { - MPFRNumber result; - MPFRNumber mpfrX(x); - mpfr_sin(result.value, mpfrX.value, MPFR_RNDN); - return result; + if (!mpfrResult.isEqual(mpfrLibcResult, t)) { + llvm::outs() << llvm::raw_ostream::RED + << "Libc result is not within tolerance value of the MPFR " + << "result:\n" + << llvm::raw_ostream::RESET; + llvm::outs() << " Input: " << mpfrInput.str() << '\n'; + llvm::outs() << "Libc result: " << mpfrLibcResult.str() << '\n'; + llvm::outs() << "MPFR result: " << mpfrResult.str() << '\n'; + + return false; } -}; -bool equalsCos(float input, float libcOutput, const Tolerance &t) { - MPFRNumber mpfrResult = MPFRNumber::cos(input); - MPFRNumber libcResult(libcOutput); - return mpfrResult.isEqual(libcResult, t); + return true; } -bool equalsSin(float input, float libcOutput, const Tolerance &t) { - MPFRNumber mpfrResult = MPFRNumber::sin(input); - MPFRNumber libcResult(libcOutput); - return mpfrResult.isEqual(libcResult, t); -} +template bool compare(Operation, float, float, const Tolerance &); +template bool compare(Operation, double, double, const Tolerance &); + +} // namespace internal } // namespace mpfr } // namespace testing diff --git a/libc/utils/UnitTest/Test.h b/libc/utils/UnitTest/Test.h --- a/libc/utils/UnitTest/Test.h +++ b/libc/utils/UnitTest/Test.h @@ -34,6 +34,7 @@ Cond_LE, Cond_GT, Cond_GE, + Cond_Extension }; namespace internal { @@ -138,8 +139,8 @@ } // namespace testing } // namespace __llvm_libc -#define TEST(SuiteName, TestName) \ - class SuiteName##_##TestName : public __llvm_libc::testing::Test { \ +#define TEST_WITH_BASE_CLASS(SuiteName, TestName, BaseClass) \ + class SuiteName##_##TestName : public BaseClass { \ public: \ SuiteName##_##TestName() { addTest(this); } \ void Run(__llvm_libc::testing::RunContext &) override; \ @@ -148,15 +149,10 @@ SuiteName##_##TestName SuiteName##_##TestName##_Instance; \ void SuiteName##_##TestName::Run(__llvm_libc::testing::RunContext &Ctx) +#define TEST(SuiteName, TestName) \ + TEST_WITH_BASE_CLASS(SuiteName, TestName, __llvm_libc::testing::Test) #define TEST_F(SuiteClass, TestName) \ - class SuiteClass##_##TestName : public SuiteClass { \ - public: \ - SuiteClass##_##TestName() { addTest(this); } \ - void Run(__llvm_libc::testing::RunContext &) override; \ - const char *getName() const override { return #SuiteClass "." #TestName; } \ - }; \ - SuiteClass##_##TestName SuiteClass##_##TestName##_Instance; \ - void SuiteClass##_##TestName::Run(__llvm_libc::testing::RunContext &Ctx) + TEST_WITH_BASE_CLASS(SuiteClass, TestName, SuiteClass) #define EXPECT_EQ(LHS, RHS) \ __llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_EQ, (LHS), \ @@ -226,6 +222,11 @@ if (!EXPECT_FALSE(VAL)) \ return +#define TEST_EXTENSION(LHS, RHS) \ + __llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_Extension, \ + (LHS), (RHS), #LHS, #RHS, __FILE__, \ + __LINE__) + #define EXPECT_EXITS(FUNC, EXIT) \ __llvm_libc::testing::Test::testProcessExits( \ Ctx, __llvm_libc::testing::Test::createCallable(FUNC), EXIT, #FUNC, \ diff --git a/libc/utils/UnitTest/Test.cpp b/libc/utils/UnitTest/Test.cpp --- a/libc/utils/UnitTest/Test.cpp +++ b/libc/utils/UnitTest/Test.cpp @@ -104,6 +104,14 @@ << "To be greater than or equal to: " << RHSStr << '\n' << " Which is: " << RHS << '\n'; return false; + case Cond_Extension: + if (LHS == RHS) + return true; + // Silently mark the check as a failure. The extension system is expected to + // report appropriate information. + Ctx.markFail(); + llvm::outs() << File << ":" << Line << ": FAILURE\n"; + return false; default: Ctx.markFail(); llvm::outs() << "Unexpected test condition.\n";