diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt index c07c6d77fa233..9eda5db1ea2fc 100644 --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -1823,6 +1823,18 @@ add_fp_unittest( libc.src.__support.FPUtil.fp_bits ) +add_fp_unittest( + fmul_test + NEED_MPFR + SUITE + libc-math-unittests + SRCS + fmul_test.cpp + HDRS + FMulTest.h + DEPENDS + libc.src.math.fmul +) add_fp_unittest( asinhf_test NEED_MPFR diff --git a/libc/test/src/math/FMulTest.h b/libc/test/src/math/FMulTest.h new file mode 100644 index 0000000000000..8ca33ea71b712 --- /dev/null +++ b/libc/test/src/math/FMulTest.h @@ -0,0 +1,121 @@ +//===-- Utility class to test fmul[f|l] -------------------------*- 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_TEST_SRC_MATH_FMULTEST_H +#define LLVM_LIBC_TEST_SRC_MATH_FMULTEST_H + +#include "src/__support/FPUtil/FPBits.h" +#include "test/UnitTest/FEnvSafeTest.h" +#include "test/UnitTest/FPMatcher.h" +#include "test/UnitTest/Test.h" +#include "utils/MPFRWrapper/MPFRUtils.h" + +namespace mpfr = LIBC_NAMESPACE::testing::mpfr; + +template +class FmulMPFRTest : public LIBC_NAMESPACE::testing::FEnvSafeTest { + + DECLARE_SPECIAL_CONSTANTS(InType) + +public: + typedef OutType (*FMulFunc)(InType, InType); + + void testFMulMPFR(FMulFunc func) { + constexpr int N = 10; + mpfr::BinaryInput INPUTS[N] = { + {3.0, 5.0}, + {0x1.0p1, 0x1.0p-131}, + {0x1.0p2, 0x1.0p-129}, + {1.0, 1.0}, + {-0.0, -0.0}, + {-0.0, 0.0}, + {0.0, -0.0}, + {0x1.0p100, 0x1.0p100}, + {1.0, 1.0 + 0x1.0p-128 + 0x1.0p-149 + 0x1.0p-150}, + {1.0, 0x1.0p-128 + 0x1.0p-149 + 0x1.0p-150}}; + + for (int i = 0; i < N; ++i) { + InType x = INPUTS[i].x; + InType y = INPUTS[i].y; + ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Fmul, INPUTS[i], + func(x, y), 0.5); + } + } + + void testSpecialInputsMPFR(FMulFunc func) { + constexpr int N = 27; + mpfr::BinaryInput INPUTS[N] = {{inf, 0x1.0p-129}, + {0x1.0p-129, inf}, + {inf, 2.0}, + {3.0, inf}, + {0.0, 0.0}, + {neg_inf, aNaN}, + {aNaN, neg_inf}, + {neg_inf, neg_inf}, + {0.0, neg_inf}, + {neg_inf, 0.0}, + {neg_inf, 1.0}, + {1.0, neg_inf}, + {neg_inf, 0x1.0p-129}, + {0x1.0p-129, neg_inf}, + {0.0, 0x1.0p-129}, + {inf, 0.0}, + {0.0, inf}, + {0.0, aNaN}, + {2.0, aNaN}, + {0x1.0p-129, aNaN}, + {inf, aNaN}, + {aNaN, aNaN}, + {0.0, sNaN}, + {2.0, sNaN}, + {0x1.0p-129, sNaN}, + {inf, sNaN}, + {sNaN, sNaN}}; + + for (int i = 0; i < N; ++i) { + InType x = INPUTS[i].x; + InType y = INPUTS[i].y; + ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Fmul, INPUTS[i], + func(x, y), 0.5); + } + } + + void testNormalRange(FMulFunc func) { + using FPBits = LIBC_NAMESPACE::fputil::FPBits; + using StorageType = typename FPBits::StorageType; + static constexpr StorageType MAX_NORMAL = FPBits::max_normal().uintval(); + static constexpr StorageType MIN_NORMAL = FPBits::min_normal().uintval(); + + constexpr StorageType COUNT = 10'001; + constexpr StorageType STEP = (MAX_NORMAL - MIN_NORMAL) / COUNT; + for (int signs = 0; signs < 4; ++signs) { + for (StorageType v = MIN_NORMAL, w = MAX_NORMAL; + v <= MAX_NORMAL && w >= MIN_NORMAL; v += STEP, w -= STEP) { + InType x = FPBits(v).get_val(), y = FPBits(w).get_val(); + if (signs % 2 == 1) { + x = -x; + } + if (signs >= 2) { + y = -y; + } + + mpfr::BinaryInput input{x, y}; + ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Fmul, input, func(x, y), + 0.5); + } + } + } +}; + +#define LIST_FMUL_MPFR_TESTS(OutType, InType, func) \ + using LlvmLibcFmulTest = FmulMPFRTest; \ + TEST_F(LlvmLibcFmulTest, MulMpfr) { testFMulMPFR(&func); } \ + TEST_F(LlvmLibcFmulTest, NanInfMpfr) { testSpecialInputsMPFR(&func); } \ + TEST_F(LlvmLibcFmulTest, NormalRange) { testNormalRange(&func); } + +#endif // LLVM_LIBC_TEST_SRC_MATH_FMULTEST_H diff --git a/libc/test/src/math/fmul_test.cpp b/libc/test/src/math/fmul_test.cpp new file mode 100644 index 0000000000000..16eaa1a818daf --- /dev/null +++ b/libc/test/src/math/fmul_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for fmul-------------------------------------------------===// +// +// 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 "FMulTest.h" + +#include "src/math/fmul.h" + +LIST_FMUL_MPFR_TESTS(float, double, LIBC_NAMESPACE::fmul) diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp index 379a631a356a3..c57cbe426425c 100644 --- a/libc/utils/MPFRWrapper/MPFRUtils.cpp +++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp @@ -487,6 +487,12 @@ class MPFRNumber { return result; } + MPFRNumber fmul(const MPFRNumber &b) { + MPFRNumber result(*this); + mpfr_mul(result.value, value, b.value, mpfr_rounding); + return result; + } + cpp::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. @@ -738,6 +744,8 @@ binary_operation_one_output(Operation op, InputType x, InputType y, return inputX.hypot(inputY); case Operation::Pow: return inputX.pow(inputY); + case Operation::Fmul: + return inputX.fmul(inputY); default: __builtin_unreachable(); } @@ -951,6 +959,9 @@ template void explain_binary_operation_one_output_error(Operation, const BinaryInput &, long double, double, RoundingMode); + +template void explain_binary_operation_one_output_error( + Operation, const BinaryInput &, float, double, RoundingMode); #ifdef LIBC_TYPES_HAS_FLOAT16 template void explain_binary_operation_one_output_error( Operation, const BinaryInput &, float16, double, RoundingMode); @@ -1126,6 +1137,10 @@ template bool compare_binary_operation_one_output(Operation, template bool compare_binary_operation_one_output(Operation, const BinaryInput &, long double, double, RoundingMode); + +template bool compare_binary_operation_one_output(Operation, + const BinaryInput &, + float, double, RoundingMode); #ifdef LIBC_TYPES_HAS_FLOAT16 template bool compare_binary_operation_one_output(Operation, const BinaryInput &, diff --git a/libc/utils/MPFRWrapper/MPFRUtils.h b/libc/utils/MPFRWrapper/MPFRUtils.h index 11e323bf6881d..743c06aa39286 100644 --- a/libc/utils/MPFRWrapper/MPFRUtils.h +++ b/libc/utils/MPFRWrapper/MPFRUtils.h @@ -76,6 +76,7 @@ enum class Operation : int { Fmod, Hypot, Pow, + Fmul, EndBinaryOperationsSingleOutput, // Operations which take two floating point numbers of the same type as @@ -237,7 +238,8 @@ class MPFRMatcher : public testing::Matcher { bool is_silent() const override { return silent; } private: - template bool match(T in, U out) { + template + bool match(InType in, OutType out) { return compare_unary_operation_single_output(op, in, out, ulp_tolerance, rounding); } @@ -259,13 +261,14 @@ class MPFRMatcher : public testing::Matcher { rounding); } - template - bool match(const TernaryInput &in, U out) { + template + bool match(const TernaryInput &in, OutType out) { return compare_ternary_operation_one_output(op, in, out, ulp_tolerance, rounding); } - template void explain_error(T in, U out) { + template + void explain_error(InType in, OutType out) { explain_unary_operation_single_output_error(op, in, out, ulp_tolerance, rounding); } @@ -287,8 +290,8 @@ class MPFRMatcher : public testing::Matcher { rounding); } - template - void explain_error(const TernaryInput &in, U out) { + template + void explain_error(const TernaryInput &in, OutType out) { explain_ternary_operation_one_output_error(op, in, out, ulp_tolerance, rounding); } @@ -304,7 +307,8 @@ constexpr bool is_valid_operation() { (op == Operation::Sqrt && cpp::is_floating_point_v && cpp::is_floating_point_v && sizeof(OutputType) <= sizeof(InputType)) || - (op == Operation::Div && internal::IsBinaryInput::VALUE && + ((op == Operation::Div || op == Operation::Fmul) && + internal::IsBinaryInput::VALUE && cpp::is_floating_point_v< typename internal::MakeScalarInput::type> && cpp::is_floating_point_v) ||