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);
}
}
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));
}
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);
}
}
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);
}
}
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));
}
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);
}
}
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.
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));
}
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);
}
}
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
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()
#include "MPFRUtils.h"
-#include <iostream>
+#include "llvm/ADT/StringRef.h"
+
#include <mpfr.h>
+#include <string>
namespace __llvm_libc {
namespace testing {
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);
}
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;
- }
+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';
+}
- static MPFRNumber sin(float x) {
- MPFRNumber result;
- MPFRNumber mpfrX(x);
- mpfr_sin(result.value, mpfrX.value, MPFR_RNDN);
- return result;
- }
+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);
};
-bool equalsCos(float input, float libcOutput, const Tolerance &t) {
- MPFRNumber mpfrResult = MPFRNumber::cos(input);
- MPFRNumber libcResult(libcOutput);
- return mpfrResult.isEqual(libcResult, t);
-}
+template bool compare<float>(Operation, float, float, const Tolerance &);
+template bool compare<double>(Operation, double, double, const Tolerance &);
-bool equalsSin(float input, float libcOutput, const Tolerance &t) {
- MPFRNumber mpfrResult = MPFRNumber::sin(input);
- MPFRNumber libcResult(libcOutput);
- return mpfrResult.isEqual(libcResult, t);
-}
+} // namespace internal
} // namespace mpfr
} // namespace testing
#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 {
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 {
+
+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) {}
-// 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);
+ 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
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <memory>
+#include <string>
namespace __llvm_libc {
namespace testutils {
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