60 changes: 32 additions & 28 deletions libc/test/src/math/sinf_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,27 @@
//
//===----------------------------------------------------------------------===//

#include "include/errno.h"
#include "include/math.h"
#include "src/errno/llvmlibc_errno.h"
#include "src/math/math_utils.h"
#include "src/math/sinf.h"
#include "test/src/math/float.h"
#include "test/src/math/sdcomp26094.h"
#include "utils/CPP/Array.h"
#include "utils/FPUtil/BitPatterns.h"
#include "utils/FPUtil/FloatOperations.h"
#include "utils/FPUtil/FloatProperties.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
#include "utils/UnitTest/Test.h"

#include <stdint.h>

using __llvm_libc::as_float;
using __llvm_libc::as_uint32_bits;
using __llvm_libc::fputil::isNegativeQuietNaN;
using __llvm_libc::fputil::isQuietNaN;
using __llvm_libc::fputil::valueAsBits;
using __llvm_libc::fputil::valueFromBits;

using BitPatterns = __llvm_libc::fputil::BitPatterns<float>;

using __llvm_libc::testing::FloatBits;
using __llvm_libc::testing::sdcomp26094Values;

namespace mpfr = __llvm_libc::testing::mpfr;
Expand All @@ -34,77 +39,76 @@ static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::floatPrecision, 12,
TEST(SinfTest, SpecialNumbers) {
llvmlibc_errno = 0;

EXPECT_TRUE(FloatBits::isQNan(
as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::QNan)))));
EXPECT_TRUE(
isQuietNaN(__llvm_libc::sinf(valueFromBits(BitPatterns::aQuietNaN))));
EXPECT_EQ(llvmlibc_errno, 0);

EXPECT_TRUE(FloatBits::isNegQNan(
as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::NegQNan)))));
EXPECT_TRUE(isNegativeQuietNaN(
__llvm_libc::sinf(valueFromBits(BitPatterns::aNegativeQuietNaN))));
EXPECT_EQ(llvmlibc_errno, 0);

EXPECT_TRUE(FloatBits::isQNan(
as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::SNan)))));
EXPECT_TRUE(isQuietNaN(
__llvm_libc::sinf(valueFromBits(BitPatterns::aSignallingNaN))));
EXPECT_EQ(llvmlibc_errno, 0);

EXPECT_TRUE(FloatBits::isNegQNan(
as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::NegSNan)))));
EXPECT_TRUE(isNegativeQuietNaN(
__llvm_libc::sinf(valueFromBits(BitPatterns::aNegativeSignallingNaN))));
EXPECT_EQ(llvmlibc_errno, 0);

EXPECT_EQ(FloatBits::Zero,
as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::Zero))));
EXPECT_EQ(BitPatterns::zero,
valueAsBits(__llvm_libc::sinf(valueFromBits(BitPatterns::zero))));
EXPECT_EQ(llvmlibc_errno, 0);

EXPECT_EQ(FloatBits::NegZero,
as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::NegZero))));
EXPECT_EQ(BitPatterns::negZero, valueAsBits(__llvm_libc::sinf(
valueFromBits(BitPatterns::negZero))));
EXPECT_EQ(llvmlibc_errno, 0);

llvmlibc_errno = 0;
EXPECT_TRUE(FloatBits::isQNan(
as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::Inf)))));
EXPECT_TRUE(isQuietNaN(__llvm_libc::sinf(valueFromBits(BitPatterns::inf))));
EXPECT_EQ(llvmlibc_errno, EDOM);

llvmlibc_errno = 0;
EXPECT_TRUE(FloatBits::isNegQNan(
as_uint32_bits(__llvm_libc::sinf(as_float(FloatBits::NegInf)))));
EXPECT_TRUE(isNegativeQuietNaN(
__llvm_libc::sinf(valueFromBits(BitPatterns::negInf))));
EXPECT_EQ(llvmlibc_errno, EDOM);
}

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);
float x = valueFromBits(v);
if (isnan(x) || isinf(x))
continue;
ASSERT_MPFR_MATCH(mpfr::OP_Sin, x, __llvm_libc::sinf(x), tolerance);
}
}

TEST(SinfTest, SpecificBitPatterns) {
float x = as_float(0xc70d39a1);
float x = valueFromBits(0xc70d39a1);
EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, __llvm_libc::sinf(x), tolerance);
}

// For small values, sin(x) is x.
TEST(SinfTest, SmallValues) {
uint32_t bits = 0x17800000;
float x = as_float(bits);
float x = valueFromBits(bits);
float result = __llvm_libc::sinf(x);
EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, result, tolerance);
EXPECT_EQ(bits, as_uint32_bits(result));
EXPECT_EQ(bits, valueAsBits(result));

bits = 0x00400000;
x = as_float(bits);
x = valueFromBits(bits);
result = __llvm_libc::sinf(x);
EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, result, tolerance);
EXPECT_EQ(bits, as_uint32_bits(result));
EXPECT_EQ(bits, valueAsBits(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) {
for (uint32_t v : sdcomp26094Values) {
float x = as_float(v);
float x = valueFromBits(v);
EXPECT_MPFR_MATCH(mpfr::OP_Sin, x, __llvm_libc::sinf(x), tolerance);
}
}
1 change: 1 addition & 0 deletions libc/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
add_subdirectory(CPP)
add_subdirectory(FPUtil)
add_subdirectory(HdrGen)
add_subdirectory(MPFRWrapper)
add_subdirectory(testutils)
Expand Down
62 changes: 62 additions & 0 deletions libc/utils/FPUtil/BitPatterns.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//===-- Bit patterns of common floating point numbers -----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_UTILS_FPUTIL_BIT_PATTERNS_H
#define LLVM_LIBC_UTILS_FPUTIL_BIT_PATTERNS_H

#include "FloatProperties.h"

namespace __llvm_libc {
namespace fputil {

template <typename T> struct BitPatterns {};

template <> struct BitPatterns<float> {
using BitsType = FloatProperties<float>::BitsType;

static constexpr BitsType inf = 0x7f800000U;
static constexpr BitsType negInf = 0xff800000U;

static constexpr BitsType zero = 0x0;
static constexpr BitsType negZero = 0x80000000U;

static constexpr BitsType one = 0x3f800000U;

// Examples of quiet NAN.
static constexpr BitsType aQuietNaN = 0x7fc00000U;
static constexpr BitsType aNegativeQuietNaN = 0xffc00000U;

// Examples of signalling NAN.
static constexpr BitsType aSignallingNaN = 0x7f800001U;
static constexpr BitsType aNegativeSignallingNaN = 0xff800001U;
};

template <> struct BitPatterns<double> {
using BitsType = FloatProperties<double>::BitsType;

static constexpr BitsType inf = 0x7ff0000000000000ULL;
static constexpr BitsType negInf = 0xfff0000000000000ULL;

static constexpr BitsType zero = 0x0ULL;
static constexpr BitsType negZero = 0x8000000000000000ULL;

static constexpr BitsType one = 0x3FF0000000000000ULL;

// Examples of quiet NAN.
static constexpr BitsType aQuietNaN = 0x7ff8000000000000ULL;
static constexpr BitsType aNegativeQuietNaN = 0xfff8000000000000ULL;

// Examples of signalling NAN.
static constexpr BitsType aSignallingNaN = 0x7ff0000000000001ULL;
static constexpr BitsType aNegativeSignallingNaN = 0xfff0000000000001ULL;
};

} // namespace fputil
} // namespace __llvm_libc

#endif // LLVM_LIBC_UTILS_FPUTIL_BIT_PATTERNS_H
9 changes: 9 additions & 0 deletions libc/utils/FPUtil/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
add_header_library(
fputil
HDRS
BitPatterns.h
FloatOperations.h
FloatProperties.h
DEPS
libc.utils.CPP.standalone_cpp
)
102 changes: 102 additions & 0 deletions libc/utils/FPUtil/FloatOperations.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//===-- Common operations on floating point numbers -------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_UTILS_FPUTIL_FLOAT_OPERATIONS_H
#define LLVM_LIBC_UTILS_FPUTIL_FLOAT_OPERATIONS_H

#include "BitPatterns.h"
#include "FloatProperties.h"

#include "utils/CPP/TypeTraits.h"

namespace __llvm_libc {
namespace fputil {

// Return the bits of a float value.
template <typename T,
cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, int> = 0>
static inline typename FloatProperties<T>::BitsType valueAsBits(T x) {
using BitsType = typename FloatProperties<T>::BitsType;
return *reinterpret_cast<BitsType *>(&x);
}

// Return the float value from bits.
template <typename BitsType,
cpp::EnableIfType<
cpp::IsFloatingPointType<FloatTypeT<BitsType>>::Value, int> = 0>
static inline FloatTypeT<BitsType> valueFromBits(BitsType bits) {
return *reinterpret_cast<FloatTypeT<BitsType> *>(&bits);
}

// Return the bits of abs(x).
template <typename T,
cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, int> = 0>
static inline typename FloatProperties<T>::BitsType absBits(T x) {
return valueAsBits(x) & (~FloatProperties<T>::signMask);
}

// Return the zero adjusted exponent value of x.
template <typename T,
cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, int> = 0>
int getExponent(T x) {
using Properties = FloatProperties<T>;
using BitsType = typename Properties::BitsType;
BitsType bits = absBits(x);
int e = (bits >> Properties::mantissaWidth); // Shift out the mantissa.
e -= Properties::exponentOffset; // Zero adjust.
return e;
}

// Return true if x is infinity (positive or negative.)
template <typename T,
cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, int> = 0>
static inline bool isInf(T x) {
using Properties = FloatProperties<T>;
using BitsType = typename FloatProperties<T>::BitsType;
BitsType bits = valueAsBits(x);
return ((bits & BitPatterns<T>::inf) == BitPatterns<T>::inf) &&
((bits & Properties::mantissaMask) == 0);
}

// Return true if x is a NAN (quiet or signalling.)
template <typename T,
cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, int> = 0>
static inline bool isNaN(T x) {
using Properties = FloatProperties<T>;
using BitsType = typename FloatProperties<T>::BitsType;
BitsType bits = valueAsBits(x);
return ((bits & BitPatterns<T>::inf) == BitPatterns<T>::inf) &&
((bits & Properties::mantissaMask) != 0);
}

// Return true if x is a quiet NAN.
template <typename T,
cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, int> = 0>
static inline bool isQuietNaN(T x) {
using Properties = FloatProperties<T>;
using BitsType = typename FloatProperties<T>::BitsType;
BitsType bits = valueAsBits(x);
return ((bits & BitPatterns<T>::inf) == BitPatterns<T>::inf) &&
((bits & Properties::quietNaNMask) != 0);
}

// Return true if x is a quiet NAN with sign bit set.
template <typename T,
cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, int> = 0>
static inline bool isNegativeQuietNaN(T x) {
using Properties = FloatProperties<T>;
using BitsType = typename FloatProperties<T>::BitsType;
BitsType bits = valueAsBits(x);
return ((bits & BitPatterns<T>::negInf) == BitPatterns<T>::negInf) &&
((bits & Properties::quietNaNMask) != 0);
}

} // namespace fputil
} // namespace __llvm_libc

#endif // LLVM_LIBC_UTILS_FPUTIL_FLOAT_OPERATIONS_H
72 changes: 72 additions & 0 deletions libc/utils/FPUtil/FloatProperties.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//===-- Properties of floating point numbers --------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_UTILS_FPUTIL_FLOAT_PROPERTIES_H
#define LLVM_LIBC_UTILS_FPUTIL_FLOAT_PROPERTIES_H

#include <stdint.h>

namespace __llvm_libc {
namespace fputil {

template <typename T> struct FloatProperties {};

template <> struct FloatProperties<float> {
typedef uint32_t BitsType;
static_assert(sizeof(BitsType) == sizeof(float),
"Unexpected size of 'float' type.");

static constexpr uint32_t mantissaWidth = 23;
static constexpr BitsType mantissaMask = 0x007fffffU;
static constexpr BitsType signMask = 0x80000000U;
static constexpr uint32_t exponentOffset = 127;

// If a number x is a NAN, then it is a quiet NAN if:
// QuietNaNMask & bits(x) != 0
// Else, it is a signalling NAN.
static constexpr BitsType quietNaNMask = 0x00400000U;
};

template <> struct FloatProperties<double> {
typedef uint64_t BitsType;
static_assert(sizeof(BitsType) == sizeof(double),
"Unexpected size of 'double' type.");

static constexpr uint32_t mantissaWidth = 52;
static constexpr BitsType mantissaMask = 0x000fffffffffffffU;
static constexpr BitsType signMask = 0x8000000000000000ULL;
static constexpr uint32_t exponentOffset = 1023;

// If a number x is a NAN, then it is a quiet NAN if:
// QuietNaNMask & bits(x) != 0
// Else, it is a signalling NAN.
static constexpr BitsType quietNaNMask = 0x0008000000000000ULL;
};

// Define the float type corresponding to the BitsType.
template <typename BitsType> struct FloatType;

template <> struct FloatType<uint32_t> {
static_assert(sizeof(uint32_t) == sizeof(float),
"Unexpected size of 'float' type.");
typedef float Type;
};

template <> struct FloatType<uint64_t> {
static_assert(sizeof(uint64_t) == sizeof(double),
"Unexpected size of 'double' type.");
typedef double Type;
};

template <typename BitsType>
using FloatTypeT = typename FloatType<BitsType>::Type;

} // namespace fputil
} // namespace __llvm_libc

#endif // LLVM_LIBC_UTILS_FPUTIL_FLOAT_PROPERTIES_H
2 changes: 1 addition & 1 deletion libc/utils/MPFRWrapper/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ if(LIBC_TESTS_CAN_USE_MPFR)
MPFRUtils.cpp
MPFRUtils.h
)
add_dependencies(libcMPFRWrapper libc.utils.CPP.standalone_cpp LibcUnitTest LLVMSupport)
add_dependencies(libcMPFRWrapper libc.utils.CPP.standalone_cpp libc.utils.FPUtil.fputil LibcUnitTest LLVMSupport)
target_link_libraries(libcMPFRWrapper -lmpfr -lgmp LibcUnitTest LLVMSupport)
else()
message(WARNING "Math tests using MPFR will be skipped.")
Expand Down
49 changes: 8 additions & 41 deletions libc/utils/MPFRWrapper/MPFRUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#include "MPFRUtils.h"

#include "utils/FPUtil/FloatOperations.h"

#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"

Expand All @@ -19,44 +21,6 @@ namespace __llvm_libc {
namespace testing {
namespace mpfr {

template <typename T> struct FloatProperties {};

template <> struct FloatProperties<float> {
typedef uint32_t BitsType;
static_assert(sizeof(BitsType) == sizeof(float),
"Unexpected size of 'float' type.");

static constexpr uint32_t mantissaWidth = 23;
static constexpr BitsType signMask = 0x7FFFFFFFU;
static constexpr uint32_t exponentOffset = 127;
};

template <> struct FloatProperties<double> {
typedef uint64_t BitsType;
static_assert(sizeof(BitsType) == sizeof(double),
"Unexpected size of 'double' type.");

static constexpr uint32_t mantissaWidth = 52;
static constexpr BitsType signMask = 0x7FFFFFFFFFFFFFFFULL;
static constexpr uint32_t exponentOffset = 1023;
};

template <typename T> typename FloatProperties<T>::BitsType getBits(T x) {
using BitsType = typename FloatProperties<T>::BitsType;
return *reinterpret_cast<BitsType *>(&x);
}

// Returns the zero adjusted exponent value of abs(x).
template <typename T> int getExponent(T x) {
using Properties = FloatProperties<T>;
using BitsType = typename Properties::BitsType;
BitsType bits = *reinterpret_cast<BitsType *>(&x);
bits &= Properties::signMask; // Zero the sign bit.
int e = (bits >> Properties::mantissaWidth); // Shift out the mantissa.
e -= Properties::exponentOffset; // Zero adjust.
return e;
}

class MPFRNumber {
// A precision value which allows sufficiently large additional
// precision even compared to double precision floating point values.
Expand Down Expand Up @@ -94,7 +58,7 @@ class MPFRNumber {
template <typename XType> MPFRNumber(XType x, const Tolerance &t) {
mpfr_init2(value, mpfrPrecision);
mpfr_set_zero(value, 1); // Set to positive zero.
MPFRNumber xExponent(getExponent(x));
MPFRNumber xExponent(fputil::getExponent(x));
// E = 2^E
mpfr_exp2(xExponent.value, xExponent.value, MPFR_RNDN);
uint32_t bitMask = 1 << (t.width - 1);
Expand Down Expand Up @@ -170,15 +134,18 @@ namespace internal {

template <typename T>
void MPFRMatcher<T>::explainError(testutils::StreamWrapper &OS) {
using fputil::valueAsBits;

MPFRNumber mpfrResult(operation, input);
MPFRNumber mpfrInput(input);
MPFRNumber mpfrMatchValue(matchValue);
MPFRNumber mpfrToleranceValue(matchValue, tolerance);
OS << "Match value not within tolerance value of MPFR result:\n"
<< " Input decimal: " << mpfrInput.str() << '\n'
<< " Input bits: 0x" << llvm::utohexstr(getBits(input)) << '\n'
<< " Input bits: 0x" << llvm::utohexstr(valueAsBits(input)) << '\n'
<< " Match decimal: " << mpfrMatchValue.str() << '\n'
<< " Match bits: 0x" << llvm::utohexstr(getBits(matchValue)) << '\n'
<< " Match bits: 0x" << llvm::utohexstr(valueAsBits(matchValue))
<< '\n'
<< " MPFR result: " << mpfrResult.str() << '\n'
<< "Tolerance value: " << mpfrToleranceValue.str() << '\n';
}
Expand Down