Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc][math][c23] Add MPFR exhaustive test for fmodf16 #94656

Merged
merged 4 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions libc/test/src/math/exhaustive/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ add_header_library(
exhaustive_test
HDRS
exhaustive_test.h
DEPENDS
libc.src.__support.CPP.type_traits
libc.src.__support.FPUtil.fp_bits
libc.src.__support.macros.properties.types
)

add_fp_unittest(
Expand Down Expand Up @@ -277,6 +281,21 @@ add_fp_unittest(
libc.src.__support.FPUtil.generic.fmod
)

add_fp_unittest(
fmodf16_test
NO_RUN_POSTBUILD
NEED_MPFR
SUITE
libc_math_exhaustive_tests
SRCS
fmodf16_test.cpp
DEPENDS
.exhaustive_test
libc.src.math.fmodf16
LINK_LIBRARIES
-lpthread
)

add_fp_unittest(
coshf_test
NO_RUN_POSTBUILD
Expand Down
124 changes: 108 additions & 16 deletions libc/test/src/math/exhaustive/exhaustive_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "src/__support/CPP/type_traits.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/macros/properties/types.h"
overmighty marked this conversation as resolved.
Show resolved Hide resolved
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
Expand Down Expand Up @@ -68,22 +69,82 @@ struct UnaryOpChecker : public virtual LIBC_NAMESPACE::testing::Test {
}
};

template <typename OutType, typename InType = OutType>
using BinaryOp = OutType(InType, InType);

template <typename OutType, typename InType, mpfr::Operation Op,
BinaryOp<OutType, InType> Func>
struct BinaryOpChecker : public virtual LIBC_NAMESPACE::testing::Test {
using FloatType = InType;
using FPBits = LIBC_NAMESPACE::fputil::FPBits<FloatType>;
using StorageType = typename FPBits::StorageType;

// Check in a range, return the number of failures.
uint64_t check(StorageType x_start, StorageType x_stop, StorageType y_start,
StorageType y_stop, mpfr::RoundingMode rounding) {
mpfr::ForceRoundingMode r(rounding);
if (!r.success)
return x_stop > x_start || y_stop > y_start;
StorageType xbits = x_start;
uint64_t failed = 0;
do {
FloatType x = FPBits(xbits).get_val();
StorageType ybits = y_start;
do {
FloatType y = FPBits(ybits).get_val();
mpfr::BinaryInput<FloatType> input{x, y};
bool correct = TEST_MPFR_MATCH_ROUNDING_SILENTLY(Op, input, Func(x, y),
0.5, rounding);
failed += (!correct);
// Uncomment to print out failed values.
// if (!correct) {
// EXPECT_MPFR_MATCH_ROUNDING(Op, input, Func(x, y), 0.5, rounding);
// }
} while (ybits++ < y_stop);
} while (xbits++ < x_stop);
return failed;
}
};

// Checker class needs inherit from LIBC_NAMESPACE::testing::Test and provide
// StorageType and check method.
template <typename Checker>
template <typename Checker, size_t Increment = 1 << 20>
struct LlvmLibcExhaustiveMathTest
: public virtual LIBC_NAMESPACE::testing::Test,
public Checker {
using FloatType = typename Checker::FloatType;
using FPBits = typename Checker::FPBits;
using StorageType = typename Checker::StorageType;

static constexpr StorageType INCREMENT = (1 << 20);
void explain_failed_range(std::stringstream &msg, StorageType x_begin,
StorageType x_end) {
#ifdef LIBC_TYPES_HAS_FLOAT16
using T = LIBC_NAMESPACE::cpp::conditional_t<
LIBC_NAMESPACE::cpp::is_same_v<FloatType, float16>, float, FloatType>;
#else
using T = FloatType;
#endif

msg << x_begin << " to " << x_end << " [0x" << std::hex << x_begin << ", 0x"
<< x_end << "), [" << std::hexfloat
<< static_cast<T>(FPBits(x_begin).get_val()) << ", "
<< static_cast<T>(FPBits(x_end).get_val()) << ")";
}

void explain_failed_range(std::stringstream &msg, StorageType x_begin,
StorageType x_end, StorageType y_begin,
StorageType y_end) {
msg << "x ";
explain_failed_range(msg, x_begin, x_end);
msg << ", y ";
explain_failed_range(msg, y_begin, y_end);
}

// Break [start, stop) into `nthreads` subintervals and apply *check to each
// subinterval in parallel.
void test_full_range(StorageType start, StorageType stop,
mpfr::RoundingMode rounding) {
template <typename... T>
void test_full_range(mpfr::RoundingMode rounding, StorageType start,
StorageType stop, T... extra_range_bounds) {
int n_threads = std::thread::hardware_concurrency();
std::vector<std::thread> thread_list;
std::mutex mx_cur_val;
Expand All @@ -102,8 +163,8 @@ struct LlvmLibcExhaustiveMathTest
return;

range_begin = current_value;
if (stop >= INCREMENT && stop - INCREMENT >= current_value) {
range_end = current_value + INCREMENT;
if (stop >= Increment && stop - Increment >= current_value) {
range_end = current_value + Increment;
} else {
range_end = stop;
}
Expand All @@ -120,15 +181,14 @@ struct LlvmLibcExhaustiveMathTest
std::cout << msg.str() << std::flush;
}

uint64_t failed_in_range =
Checker::check(range_begin, range_end, rounding);
uint64_t failed_in_range = Checker::check(
range_begin, range_end, extra_range_bounds..., rounding);
if (failed_in_range > 0) {
std::stringstream msg;
msg << "Test failed for " << std::dec << failed_in_range
<< " inputs in range: " << range_begin << " to " << range_end
<< " [0x" << std::hex << range_begin << ", 0x" << range_end
<< "), [" << std::hexfloat << FPBits(range_begin).get_val()
<< ", " << FPBits(range_end).get_val() << ")\n";
<< " inputs in range: ";
explain_failed_range(msg, start, stop, extra_range_bounds...);
msg << "\n";
std::cerr << msg.str() << std::flush;

failed.fetch_add(failed_in_range);
Expand All @@ -151,19 +211,46 @@ struct LlvmLibcExhaustiveMathTest
void test_full_range_all_roundings(StorageType start, StorageType stop) {
std::cout << "-- Testing for FE_TONEAREST in range [0x" << std::hex << start
<< ", 0x" << stop << ") --" << std::dec << std::endl;
test_full_range(start, stop, mpfr::RoundingMode::Nearest);
test_full_range(mpfr::RoundingMode::Nearest, start, stop);

std::cout << "-- Testing for FE_UPWARD in range [0x" << std::hex << start
<< ", 0x" << stop << ") --" << std::dec << std::endl;
test_full_range(start, stop, mpfr::RoundingMode::Upward);
test_full_range(mpfr::RoundingMode::Upward, start, stop);

std::cout << "-- Testing for FE_DOWNWARD in range [0x" << std::hex << start
<< ", 0x" << stop << ") --" << std::dec << std::endl;
test_full_range(start, stop, mpfr::RoundingMode::Downward);
test_full_range(mpfr::RoundingMode::Downward, start, stop);

std::cout << "-- Testing for FE_TOWARDZERO in range [0x" << std::hex
<< start << ", 0x" << stop << ") --" << std::dec << std::endl;
test_full_range(start, stop, mpfr::RoundingMode::TowardZero);
test_full_range(mpfr::RoundingMode::TowardZero, start, stop);
};

void test_full_range_all_roundings(StorageType x_start, StorageType x_stop,
StorageType y_start, StorageType y_stop) {
std::cout << "-- Testing for FE_TONEAREST in x range [0x" << std::hex
<< x_start << ", 0x" << x_stop << "), y range [0x" << y_start
<< ", 0x" << y_stop << ") --" << std::dec << std::endl;
test_full_range(mpfr::RoundingMode::Nearest, x_start, x_stop, y_start,
y_stop);

std::cout << "-- Testing for FE_UPWARD in x range [0x" << std::hex
<< x_start << ", 0x" << x_stop << "), y range [0x" << y_start
<< ", 0x" << y_stop << ") --" << std::dec << std::endl;
test_full_range(mpfr::RoundingMode::Upward, x_start, x_stop, y_start,
y_stop);

std::cout << "-- Testing for FE_DOWNWARD in x range [0x" << std::hex
<< x_start << ", 0x" << x_stop << "), y range [0x" << y_start
<< ", 0x" << y_stop << ") --" << std::dec << std::endl;
test_full_range(mpfr::RoundingMode::Downward, x_start, x_stop, y_start,
y_stop);

std::cout << "-- Testing for FE_TOWARDZERO in x range [0x" << std::hex
<< x_start << ", 0x" << x_stop << "), y range [0x" << y_start
<< ", 0x" << y_stop << ") --" << std::dec << std::endl;
test_full_range(mpfr::RoundingMode::TowardZero, x_start, x_stop, y_start,
y_stop);
};
};

Expand All @@ -175,3 +262,8 @@ template <typename OutType, typename InType, mpfr::Operation Op,
UnaryOp<OutType, InType> Func>
using LlvmLibcUnaryNarrowingOpExhaustiveMathTest =
LlvmLibcExhaustiveMathTest<UnaryOpChecker<OutType, InType, Op, Func>>;

template <typename FloatType, mpfr::Operation Op, BinaryOp<FloatType> Func>
using LlvmLibcBinaryOpExhaustiveMathTest =
LlvmLibcExhaustiveMathTest<BinaryOpChecker<FloatType, FloatType, Op, Func>,
1 << 2>;
41 changes: 41 additions & 0 deletions libc/test/src/math/exhaustive/fmodf16_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//===-- Exhaustive test for fmodf16 ---------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#include "exhaustive_test.h"
#include "src/math/fmodf16.h"
#include "utils/MPFRWrapper/MPFRUtils.h"

namespace mpfr = LIBC_NAMESPACE::testing::mpfr;

using LlvmLibcFmodf16ExhaustiveTest =
LlvmLibcBinaryOpExhaustiveMathTest<float16, mpfr::Operation::Fmod,
LIBC_NAMESPACE::fmodf16>;

// Range: [0, Inf];
static constexpr uint16_t POS_START = 0x0000U;
static constexpr uint16_t POS_STOP = 0x7c00U;

// Range: [-Inf, 0];
static constexpr uint16_t NEG_START = 0x8000U;
static constexpr uint16_t NEG_STOP = 0xfc00U;

TEST_F(LlvmLibcFmodf16ExhaustiveTest, PostivePositiveRange) {
test_full_range_all_roundings(POS_START, POS_STOP, POS_START, POS_STOP);
}

TEST_F(LlvmLibcFmodf16ExhaustiveTest, PostiveNegativeRange) {
test_full_range_all_roundings(POS_START, POS_STOP, NEG_START, NEG_STOP);
}

TEST_F(LlvmLibcFmodf16ExhaustiveTest, NegativePositiveRange) {
test_full_range_all_roundings(NEG_START, NEG_STOP, POS_START, POS_STOP);
}

TEST_F(LlvmLibcFmodf16ExhaustiveTest, NegativeNegativeRange) {
test_full_range_all_roundings(NEG_START, NEG_STOP, POS_START, POS_STOP);
}
6 changes: 6 additions & 0 deletions libc/utils/MPFRWrapper/MPFRUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,8 @@ explain_binary_operation_one_output_error(Operation,
const BinaryInput<long double> &,
long double, double, RoundingMode);
#ifdef LIBC_TYPES_HAS_FLOAT16
template void explain_binary_operation_one_output_error(
Operation, const BinaryInput<float16> &, float16, double, RoundingMode);
template void
explain_binary_operation_one_output_error(Operation, const BinaryInput<float> &,
float16, double, RoundingMode);
Expand Down Expand Up @@ -1088,6 +1090,10 @@ template bool
compare_binary_operation_one_output(Operation, const BinaryInput<long double> &,
long double, double, RoundingMode);
#ifdef LIBC_TYPES_HAS_FLOAT16
template bool compare_binary_operation_one_output(Operation,
const BinaryInput<float16> &,
float16, double,
RoundingMode);
template bool compare_binary_operation_one_output(Operation,
const BinaryInput<float> &,
float16, double,
Expand Down
Loading