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 >
74112struct 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>
176263using 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 >;
0 commit comments