Skip to content

Commit ef05b03

Browse files
authored
[libc][math][c23] Add MPFR exhaustive test for fmodf16 (#94656)
1 parent 3c24eb3 commit ef05b03

File tree

4 files changed

+174
-16
lines changed

4 files changed

+174
-16
lines changed

libc/test/src/math/exhaustive/CMakeLists.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ add_header_library(
44
exhaustive_test
55
HDRS
66
exhaustive_test.h
7+
DEPENDS
8+
libc.src.__support.CPP.type_traits
9+
libc.src.__support.FPUtil.fp_bits
10+
libc.src.__support.macros.properties.types
711
)
812

913
add_fp_unittest(
@@ -277,6 +281,21 @@ add_fp_unittest(
277281
libc.src.__support.FPUtil.generic.fmod
278282
)
279283

284+
add_fp_unittest(
285+
fmodf16_test
286+
NO_RUN_POSTBUILD
287+
NEED_MPFR
288+
SUITE
289+
libc_math_exhaustive_tests
290+
SRCS
291+
fmodf16_test.cpp
292+
DEPENDS
293+
.exhaustive_test
294+
libc.src.math.fmodf16
295+
LINK_LIBRARIES
296+
-lpthread
297+
)
298+
280299
add_fp_unittest(
281300
coshf_test
282301
NO_RUN_POSTBUILD

libc/test/src/math/exhaustive/exhaustive_test.h

Lines changed: 108 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "src/__support/CPP/type_traits.h"
1010
#include "src/__support/FPUtil/FPBits.h"
11+
#include "src/__support/macros/properties/types.h"
1112
#include "test/UnitTest/FPMatcher.h"
1213
#include "test/UnitTest/Test.h"
1314
#include "utils/MPFRWrapper/MPFRUtils.h"
@@ -68,22 +69,82 @@ struct UnaryOpChecker : public virtual LIBC_NAMESPACE::testing::Test {
6869
}
6970
};
7071

72+
template <typename OutType, typename InType = OutType>
73+
using BinaryOp = OutType(InType, InType);
74+
75+
template <typename OutType, typename InType, mpfr::Operation Op,
76+
BinaryOp<OutType, InType> Func>
77+
struct BinaryOpChecker : public virtual LIBC_NAMESPACE::testing::Test {
78+
using FloatType = InType;
79+
using FPBits = LIBC_NAMESPACE::fputil::FPBits<FloatType>;
80+
using StorageType = typename FPBits::StorageType;
81+
82+
// Check in a range, return the number of failures.
83+
uint64_t check(StorageType x_start, StorageType x_stop, StorageType y_start,
84+
StorageType y_stop, mpfr::RoundingMode rounding) {
85+
mpfr::ForceRoundingMode r(rounding);
86+
if (!r.success)
87+
return x_stop > x_start || y_stop > y_start;
88+
StorageType xbits = x_start;
89+
uint64_t failed = 0;
90+
do {
91+
FloatType x = FPBits(xbits).get_val();
92+
StorageType ybits = y_start;
93+
do {
94+
FloatType y = FPBits(ybits).get_val();
95+
mpfr::BinaryInput<FloatType> input{x, y};
96+
bool correct = TEST_MPFR_MATCH_ROUNDING_SILENTLY(Op, input, Func(x, y),
97+
0.5, rounding);
98+
failed += (!correct);
99+
// Uncomment to print out failed values.
100+
// if (!correct) {
101+
// EXPECT_MPFR_MATCH_ROUNDING(Op, input, Func(x, y), 0.5, rounding);
102+
// }
103+
} while (ybits++ < y_stop);
104+
} while (xbits++ < x_stop);
105+
return failed;
106+
}
107+
};
108+
71109
// Checker class needs inherit from LIBC_NAMESPACE::testing::Test and provide
72110
// StorageType and check method.
73-
template <typename Checker>
111+
template <typename Checker, size_t Increment = 1 << 20>
74112
struct LlvmLibcExhaustiveMathTest
75113
: public virtual LIBC_NAMESPACE::testing::Test,
76114
public Checker {
77115
using FloatType = typename Checker::FloatType;
78116
using FPBits = typename Checker::FPBits;
79117
using StorageType = typename Checker::StorageType;
80118

81-
static constexpr StorageType INCREMENT = (1 << 20);
119+
void explain_failed_range(std::stringstream &msg, StorageType x_begin,
120+
StorageType x_end) {
121+
#ifdef LIBC_TYPES_HAS_FLOAT16
122+
using T = LIBC_NAMESPACE::cpp::conditional_t<
123+
LIBC_NAMESPACE::cpp::is_same_v<FloatType, float16>, float, FloatType>;
124+
#else
125+
using T = FloatType;
126+
#endif
127+
128+
msg << x_begin << " to " << x_end << " [0x" << std::hex << x_begin << ", 0x"
129+
<< x_end << "), [" << std::hexfloat
130+
<< static_cast<T>(FPBits(x_begin).get_val()) << ", "
131+
<< static_cast<T>(FPBits(x_end).get_val()) << ")";
132+
}
133+
134+
void explain_failed_range(std::stringstream &msg, StorageType x_begin,
135+
StorageType x_end, StorageType y_begin,
136+
StorageType y_end) {
137+
msg << "x ";
138+
explain_failed_range(msg, x_begin, x_end);
139+
msg << ", y ";
140+
explain_failed_range(msg, y_begin, y_end);
141+
}
82142

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

104165
range_begin = current_value;
105-
if (stop >= INCREMENT && stop - INCREMENT >= current_value) {
106-
range_end = current_value + INCREMENT;
166+
if (stop >= Increment && stop - Increment >= current_value) {
167+
range_end = current_value + Increment;
107168
} else {
108169
range_end = stop;
109170
}
@@ -120,15 +181,14 @@ struct LlvmLibcExhaustiveMathTest
120181
std::cout << msg.str() << std::flush;
121182
}
122183

123-
uint64_t failed_in_range =
124-
Checker::check(range_begin, range_end, rounding);
184+
uint64_t failed_in_range = Checker::check(
185+
range_begin, range_end, extra_range_bounds..., rounding);
125186
if (failed_in_range > 0) {
126187
std::stringstream msg;
127188
msg << "Test failed for " << std::dec << failed_in_range
128-
<< " inputs in range: " << range_begin << " to " << range_end
129-
<< " [0x" << std::hex << range_begin << ", 0x" << range_end
130-
<< "), [" << std::hexfloat << FPBits(range_begin).get_val()
131-
<< ", " << FPBits(range_end).get_val() << ")\n";
189+
<< " inputs in range: ";
190+
explain_failed_range(msg, start, stop, extra_range_bounds...);
191+
msg << "\n";
132192
std::cerr << msg.str() << std::flush;
133193

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

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

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

164224
std::cout << "-- Testing for FE_TOWARDZERO in range [0x" << std::hex
165225
<< start << ", 0x" << stop << ") --" << std::dec << std::endl;
166-
test_full_range(start, stop, mpfr::RoundingMode::TowardZero);
226+
test_full_range(mpfr::RoundingMode::TowardZero, start, stop);
227+
};
228+
229+
void test_full_range_all_roundings(StorageType x_start, StorageType x_stop,
230+
StorageType y_start, StorageType y_stop) {
231+
std::cout << "-- Testing for FE_TONEAREST in x range [0x" << std::hex
232+
<< x_start << ", 0x" << x_stop << "), y range [0x" << y_start
233+
<< ", 0x" << y_stop << ") --" << std::dec << std::endl;
234+
test_full_range(mpfr::RoundingMode::Nearest, x_start, x_stop, y_start,
235+
y_stop);
236+
237+
std::cout << "-- Testing for FE_UPWARD in x range [0x" << std::hex
238+
<< x_start << ", 0x" << x_stop << "), y range [0x" << y_start
239+
<< ", 0x" << y_stop << ") --" << std::dec << std::endl;
240+
test_full_range(mpfr::RoundingMode::Upward, x_start, x_stop, y_start,
241+
y_stop);
242+
243+
std::cout << "-- Testing for FE_DOWNWARD in x range [0x" << std::hex
244+
<< x_start << ", 0x" << x_stop << "), y range [0x" << y_start
245+
<< ", 0x" << y_stop << ") --" << std::dec << std::endl;
246+
test_full_range(mpfr::RoundingMode::Downward, x_start, x_stop, y_start,
247+
y_stop);
248+
249+
std::cout << "-- Testing for FE_TOWARDZERO in x range [0x" << std::hex
250+
<< x_start << ", 0x" << x_stop << "), y range [0x" << y_start
251+
<< ", 0x" << y_stop << ") --" << std::dec << std::endl;
252+
test_full_range(mpfr::RoundingMode::TowardZero, x_start, x_stop, y_start,
253+
y_stop);
167254
};
168255
};
169256

@@ -175,3 +262,8 @@ template <typename OutType, typename InType, mpfr::Operation Op,
175262
UnaryOp<OutType, InType> Func>
176263
using LlvmLibcUnaryNarrowingOpExhaustiveMathTest =
177264
LlvmLibcExhaustiveMathTest<UnaryOpChecker<OutType, InType, Op, Func>>;
265+
266+
template <typename FloatType, mpfr::Operation Op, BinaryOp<FloatType> Func>
267+
using LlvmLibcBinaryOpExhaustiveMathTest =
268+
LlvmLibcExhaustiveMathTest<BinaryOpChecker<FloatType, FloatType, Op, Func>,
269+
1 << 2>;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//===-- Exhaustive test for fmodf16 ---------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "exhaustive_test.h"
10+
#include "src/math/fmodf16.h"
11+
#include "utils/MPFRWrapper/MPFRUtils.h"
12+
13+
namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
14+
15+
using LlvmLibcFmodf16ExhaustiveTest =
16+
LlvmLibcBinaryOpExhaustiveMathTest<float16, mpfr::Operation::Fmod,
17+
LIBC_NAMESPACE::fmodf16>;
18+
19+
// Range: [0, Inf];
20+
static constexpr uint16_t POS_START = 0x0000U;
21+
static constexpr uint16_t POS_STOP = 0x7c00U;
22+
23+
// Range: [-Inf, 0];
24+
static constexpr uint16_t NEG_START = 0x8000U;
25+
static constexpr uint16_t NEG_STOP = 0xfc00U;
26+
27+
TEST_F(LlvmLibcFmodf16ExhaustiveTest, PostivePositiveRange) {
28+
test_full_range_all_roundings(POS_START, POS_STOP, POS_START, POS_STOP);
29+
}
30+
31+
TEST_F(LlvmLibcFmodf16ExhaustiveTest, PostiveNegativeRange) {
32+
test_full_range_all_roundings(POS_START, POS_STOP, NEG_START, NEG_STOP);
33+
}
34+
35+
TEST_F(LlvmLibcFmodf16ExhaustiveTest, NegativePositiveRange) {
36+
test_full_range_all_roundings(NEG_START, NEG_STOP, POS_START, POS_STOP);
37+
}
38+
39+
TEST_F(LlvmLibcFmodf16ExhaustiveTest, NegativeNegativeRange) {
40+
test_full_range_all_roundings(NEG_START, NEG_STOP, POS_START, POS_STOP);
41+
}

libc/utils/MPFRWrapper/MPFRUtils.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,8 @@ explain_binary_operation_one_output_error(Operation,
930930
const BinaryInput<long double> &,
931931
long double, double, RoundingMode);
932932
#ifdef LIBC_TYPES_HAS_FLOAT16
933+
template void explain_binary_operation_one_output_error(
934+
Operation, const BinaryInput<float16> &, float16, double, RoundingMode);
933935
template void
934936
explain_binary_operation_one_output_error(Operation, const BinaryInput<float> &,
935937
float16, double, RoundingMode);
@@ -1088,6 +1090,10 @@ template bool
10881090
compare_binary_operation_one_output(Operation, const BinaryInput<long double> &,
10891091
long double, double, RoundingMode);
10901092
#ifdef LIBC_TYPES_HAS_FLOAT16
1093+
template bool compare_binary_operation_one_output(Operation,
1094+
const BinaryInput<float16> &,
1095+
float16, double,
1096+
RoundingMode);
10911097
template bool compare_binary_operation_one_output(Operation,
10921098
const BinaryInput<float> &,
10931099
float16, double,

0 commit comments

Comments
 (0)