diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt --- a/libc/config/darwin/arm/entrypoints.txt +++ b/libc/config/darwin/arm/entrypoints.txt @@ -105,6 +105,8 @@ libc.src.fenv.feupdateenv # math.h entrypoints + libc.src.math.atanf + libc.src.math.atanhf libc.src.math.copysign libc.src.math.copysignf libc.src.math.copysignl diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -149,6 +149,8 @@ libc.src.fenv.feupdateenv # math.h entrypoints + libc.src.math.atanf + libc.src.math.atanhf libc.src.math.copysign libc.src.math.copysignf libc.src.math.copysignl diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -149,6 +149,8 @@ libc.src.fenv.feupdateenv # math.h entrypoints + libc.src.math.atanf + libc.src.math.atanhf libc.src.math.copysign libc.src.math.copysignf libc.src.math.copysignl diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt --- a/libc/config/windows/entrypoints.txt +++ b/libc/config/windows/entrypoints.txt @@ -106,6 +106,8 @@ libc.src.fenv.feupdateenv # math.h entrypoints + libc.src.math.atanf + libc.src.math.atanhf libc.src.math.copysign libc.src.math.copysignf libc.src.math.copysignl diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -479,6 +479,10 @@ FunctionSpec<"coshf", RetValSpec, [ArgSpec]>, FunctionSpec<"sinhf", RetValSpec, [ArgSpec]>, FunctionSpec<"tanhf", RetValSpec, [ArgSpec]>, + + FunctionSpec<"atanf", RetValSpec, [ArgSpec]>, + + FunctionSpec<"atanhf", RetValSpec, [ArgSpec]>, ] >; diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h --- a/libc/src/__support/FPUtil/FPBits.h +++ b/libc/src/__support/FPUtil/FPBits.h @@ -196,6 +196,15 @@ } return result; } + + inline static FPBits create_value(bool sign, UIntType unbiased_exp, + UIntType mantissa) { + FPBits result; + result.set_sign(sign); + result.set_unbiased_exponent(unbiased_exp); + result.set_mantissa(mantissa); + return result; + } }; } // namespace fputil diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt --- a/libc/src/math/CMakeLists.txt +++ b/libc/src/math/CMakeLists.txt @@ -66,6 +66,9 @@ -O3 ) +add_math_entrypoint_object(atanf) +add_math_entrypoint_object(atanhf) + add_math_entrypoint_object(ceil) add_math_entrypoint_object(ceilf) add_math_entrypoint_object(ceill) diff --git a/libc/src/math/atanf.h b/libc/src/math/atanf.h new file mode 100644 --- /dev/null +++ b/libc/src/math/atanf.h @@ -0,0 +1,18 @@ +//===-- Implementation header for atanf -------------------------*- 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_SRC_MATH_ATANF_H +#define LLVM_LIBC_SRC_MATH_ATANF_H + +namespace __llvm_libc { + +float atanf(float x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_ATANF_H diff --git a/libc/src/math/atanhf.h b/libc/src/math/atanhf.h new file mode 100644 --- /dev/null +++ b/libc/src/math/atanhf.h @@ -0,0 +1,18 @@ +//===-- Implementation header for atanhf ------------------------*- 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_SRC_MATH_ATANHF_H +#define LLVM_LIBC_SRC_MATH_ATANHF_H + +namespace __llvm_libc { + +float atanhf(float x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_ATANHF_H diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -1225,3 +1225,37 @@ -O3 ) +add_entrypoint_object( + atanhf + SRCS + atanhf.cpp + HDRS + ../atanhf.h + DEPENDS + .supfuncf + libc.src.__support.FPUtil.fputil + libc.src.__support.FPUtil.multiply_add + libc.src.__support.FPUtil.nearest_integer + libc.src.__support.FPUtil.polyeval + libc.include.math + COMPILE_OPTIONS + -O3 +) + +add_entrypoint_object( + atanf + SRCS + atanf.cpp + HDRS + ../atanf.h + DEPENDS + .supfuncf + libc.src.__support.FPUtil.fputil + libc.src.__support.FPUtil.multiply_add + libc.src.__support.FPUtil.nearest_integer + libc.src.__support.FPUtil.polyeval + libc.include.math + COMPILE_OPTIONS + -O3 +) + diff --git a/libc/src/math/generic/atanf.cpp b/libc/src/math/generic/atanf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/generic/atanf.cpp @@ -0,0 +1,51 @@ +//===-- Single-precision atan function ------------------------------------===// +// +// 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 "src/math/atanf.h" +#include "math_utils.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/math/generic/supfuncf.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(float, atanf, (float x)) { + using FPBits = typename fputil::FPBits; + FPBits xbits(x); + bool sign = xbits.get_sign(); + xbits.set_sign(false); + + if (unlikely(xbits.is_inf_or_nan())) { + if (xbits.is_inf()) + return opt_barrier(sign ? -M_PI_2 : M_PI_2); + else + return x + 1.0f; + } + + if (unlikely(xbits.uintval() == 0x3d8d6b23U)) { + if (fputil::get_round() == FE_TONEAREST) { + FPBits br(0x3d8d31c3U); + br.set_sign(sign); + return br.get_val(); + } + } + + if (unlikely(xbits.uintval() == 0x3feefcfbU)) { + int rounding_mode = fputil::get_round(); + if (sign) { + if (rounding_mode == FE_DOWNWARD) + return FPBits(0xbf8a1f63U).get_val(); + } else { + if (rounding_mode == FE_UPWARD) + return FPBits(0x3f8a1f63U).get_val(); + } + } + + return atan_eval(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/generic/atanhf.cpp b/libc/src/math/generic/atanhf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/generic/atanhf.cpp @@ -0,0 +1,56 @@ +//===-- Single-precision atanh function -----------------------------------===// +// +// 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 "src/math/atanhf.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/math/generic/supfuncf.h" + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(float, atanhf, (float x)) { + using FPBits = typename fputil::FPBits; + FPBits xbits(x); + bool sign = xbits.get_sign(); + uint32_t x_abs = xbits.uintval() & FPBits::FloatProp::EXP_MANT_MASK; + + // 0 >= 1.0 + if (unlikely(x_abs >= 0x3F80'0000U)) { + if (xbits.is_nan()) { + return x + 1.0f; + } + if (x_abs == 0x3F80'0000U) { + fputil::set_except(FE_DIVBYZERO); + return with_errno(FPBits::inf(sign).get_val(), ERANGE); + } else { + fputil::set_except(FE_INVALID); + return with_errno( + FPBits::build_nan(1 << (fputil::MantissaWidth::VALUE - 1)), + EDOM); + } + } + + // |x| < ~0.10 + if (unlikely(x_abs <= 0x3dcc'0000U)) { + // |x| <= 2^-26 + if (unlikely(x_abs <= 0x3280'0000U)) { + return unlikely(x_abs == 0) ? x : (x + 0x1.5555555555555p-2 * x * x * x); + } + + double xdbl = x; + double x2 = xdbl * xdbl; + // Pure Taylor series. + double pe = fputil::polyeval(x2, 0.0, 0x1.5555555555555p-2, + 0x1.999999999999ap-3, 0x1.2492492492492p-3, + 0x1.c71c71c71c71cp-4, 0x1.745d1745d1746p-4); + return fputil::multiply_add(xdbl, pe, xdbl); + } + double xdbl = x; + return 0.5 * log_eval((xdbl + 1.0) / (xdbl - 1.0)); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/generic/supfuncf.h b/libc/src/math/generic/supfuncf.h --- a/libc/src/math/generic/supfuncf.h +++ b/libc/src/math/generic/supfuncf.h @@ -70,6 +70,14 @@ double r; }; +// atan table size +constexpr int ATAN_T_BITS = 4; +constexpr int ATAN_T_SIZE = 1 << ATAN_T_BITS; + +// N[Table[ArcTan[x], {x, 1/8, 8/8, 1/8}], 40] +extern const double ATAN_T[ATAN_T_SIZE]; +extern const double ATAN_K[5]; + // The function correctly calculates exp value with at least float precision // in range not narrow than [-log(2^-150), 90] template @@ -154,6 +162,63 @@ return log2_eval(x) * 0x1.62e42fefa39efp-1; } +// The main idea of the function is to use formula +// atan(u) + atan(v) = atan((u+v)/(1-uv)) + +// x should be positive, normal finite value +inline static double atan_eval(double x) { + using FPB = fputil::FPBits; + // Added some small value to umin and umax mantissa to avoid possible rounding + // errors. + FPB::UIntType umin = + FPB::create_value(false, FPB::EXPONENT_BIAS - ATAN_T_BITS - 1, + 0x100000000000UL) + .uintval(); + FPB::UIntType umax = + FPB::create_value(false, FPB::EXPONENT_BIAS + ATAN_T_BITS, + 0xF000000000000UL) + .uintval(); + + FPB bs(x); + bool sign = bs.get_sign(); + auto x_abs = bs.uintval() & FPB::FloatProp::EXP_MANT_MASK; + + if (x_abs <= umin) { + double pe = __llvm_libc::fputil::polyeval(x * x, 0.0, ATAN_K[1], ATAN_K[2], + ATAN_K[3], ATAN_K[4]); + return fputil::multiply_add(pe, x, x); + } + + if (x_abs >= umax) { + double one_over_x_m = -1.0 / x; + double one_over_x2 = one_over_x_m * one_over_x_m; + double pe = __llvm_libc::fputil::polyeval(one_over_x2, ATAN_K[0], ATAN_K[1], + ATAN_K[2], ATAN_K[3]); + return fputil::multiply_add(pe, one_over_x_m, sign ? (-M_PI_2) : (M_PI_2)); + } + + double pos_x = FPB(x_abs).get_val(); + bool one_over_x = pos_x > 1.0; + if (one_over_x) { + pos_x = 1.0 / pos_x; + } + + double near_x = fputil::nearest_integer(pos_x * ATAN_T_SIZE); + int val = static_cast(near_x); + near_x *= 1.0 / ATAN_T_SIZE; + + double v = (pos_x - near_x) / fputil::multiply_add(near_x, pos_x, 1.0); + double v2 = v * v; + double pe = __llvm_libc::fputil::polyeval(v2, ATAN_K[0], ATAN_K[1], ATAN_K[2], + ATAN_K[3], ATAN_K[4]); + double result; + if (one_over_x) + result = M_PI_2 - fputil::multiply_add(pe, v, ATAN_T[val - 1]); + else + result = fputil::multiply_add(pe, v, ATAN_T[val - 1]); + return sign ? -result : result; +} + } // namespace __llvm_libc #endif // LLVM_LIBC_SRC_MATH_GENERIC_EXPLOGXF_H diff --git a/libc/src/math/generic/supfuncf.cpp b/libc/src/math/generic/supfuncf.cpp --- a/libc/src/math/generic/supfuncf.cpp +++ b/libc/src/math/generic/supfuncf.cpp @@ -86,4 +86,19 @@ double K_LOG2_EVEN[4] = {-0x1.71547652b82fep-1, -0x1.71547652b82fep-2, -0x1.ec709dc3a03fdp-3, -0x1.2776c50ef9bfep-3}; +// N[Table[ArcTan[x], {x, 1/16, 16/16, 1/16}], 40] +alignas(64) const double ATAN_T[ATAN_T_SIZE] = { + 0x1.ff55bb72cfdeap-5, 0x1.fd5ba9aac2f6ep-4, 0x1.7b97b4bce5b02p-3, + 0x1.f5b75f92c80ddp-3, 0x1.362773707ebccp-2, 0x1.6f61941e4def1p-2, + 0x1.a64eec3cc23fdp-2, 0x1.dac670561bb4fp-2, 0x1.0657e94db30d0p-1, + 0x1.1e00babdefeb4p-1, 0x1.345f01cce37bbp-1, 0x1.4978fa3269ee1p-1, + 0x1.5d58987169b18p-1, 0x1.700a7c5784634p-1, 0x1.819d0b7158a4dp-1, + 0x1.921fb54442d18p-1}; + +// for(int i = 0; i < 5; i++) +// printf("%.13a,\n", (-2 * (i % 2) + 1) * 1.0 / (2 * i + 1)); +alignas(64) const double ATAN_K[5] = { + 0x1.0000000000000p+0, -0x1.5555555555555p-2, 0x1.999999999999ap-3, + -0x1.2492492492492p-3, 0x1.c71c71c71c71cp-4}; + } // namespace __llvm_libc diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -1374,6 +1374,30 @@ libc.src.__support.FPUtil.fputil ) +add_fp_unittest( + atanhf_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + atanhf_test.cpp + DEPENDS + libc.src.math.atanhf + libc.src.__support.FPUtil.fputil +) + +add_fp_unittest( + atanf_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + atanf_test.cpp + DEPENDS + libc.src.math.atanf + libc.src.__support.FPUtil.fputil +) + add_subdirectory(generic) add_subdirectory(exhaustive) add_subdirectory(differential_testing) diff --git a/libc/test/src/math/atanf_test.cpp b/libc/test/src/math/atanf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/atanf_test.cpp @@ -0,0 +1,65 @@ +//===-- Unittests for atanf -----------------------------------------------===// +// +// 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 "src/__support/FPUtil/FPBits.h" +#include "src/math/atanf.h" +#include "utils/MPFRWrapper/MPFRUtils.h" +#include "utils/UnitTest/FPMatcher.h" +#include "utils/UnitTest/Test.h" +#include + +#include +#include + +#include + +using FPBits = __llvm_libc::fputil::FPBits; + +namespace mpfr = __llvm_libc::testing::mpfr; + +DECLARE_SPECIAL_CONSTANTS(float) + +TEST(LlvmLibcAtanfTest, SpecialNumbers) { + errno = 0; + __llvm_libc::fputil::clear_except(FE_ALL_EXCEPT); + EXPECT_FP_EQ(aNaN, __llvm_libc::atanf(aNaN)); + EXPECT_FP_EXCEPTION(FE_INVALID); + EXPECT_MATH_ERRNO(0); + + __llvm_libc::fputil::clear_except(FE_ALL_EXCEPT); + EXPECT_FP_EQ(0.0f, __llvm_libc::atanf(0.0f)); + EXPECT_FP_EXCEPTION(0); + EXPECT_MATH_ERRNO(0); + + __llvm_libc::fputil::clear_except(FE_ALL_EXCEPT); + EXPECT_FP_EQ(-0.0f, __llvm_libc::atanf(-0.0f)); + EXPECT_FP_EXCEPTION(0); + EXPECT_MATH_ERRNO(0); +} + +TEST(LlvmLibcAtanfTest, InFloatRange) { + constexpr uint32_t COUNT = 1000000; + const uint32_t STEP = FPBits(inf).uintval() / COUNT; + for (uint32_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) { + float x = float(FPBits(v)); + EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan, x, + __llvm_libc::atanf(x), 0.5); + EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan, -x, + __llvm_libc::atanf(-x), 0.5); + } +} + +// For small values, tanh(x) is x. +TEST(LlvmLibcAtanfTest, SpecialValues) { + for (uint32_t v : {0x3d8d6b23U, 0x3feefcfbU, 0xbd8d6b23U, 0xbfeefcfbU, + 0x7F800000U, 0xFF800000U}) { + float x = float(FPBits(v)); + EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan, x, + __llvm_libc::atanf(x), 0.5); + } +} diff --git a/libc/test/src/math/atanhf_test.cpp b/libc/test/src/math/atanhf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/atanhf_test.cpp @@ -0,0 +1,108 @@ +//===-- Unittests for atanhf ----------------------------------------------===// +// +// 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 "src/__support/FPUtil/FPBits.h" +#include "src/math/atanhf.h" +#include "utils/MPFRWrapper/MPFRUtils.h" +#include "utils/UnitTest/FPMatcher.h" +#include "utils/UnitTest/Test.h" +#include + +#include +#include + +using FPBits = __llvm_libc::fputil::FPBits; + +namespace mpfr = __llvm_libc::testing::mpfr; + +DECLARE_SPECIAL_CONSTANTS(float) + +TEST(LlvmLibcAtanhfTest, SpecialNumbers) { + errno = 0; + __llvm_libc::fputil::clear_except(FE_ALL_EXCEPT); + EXPECT_FP_EQ(aNaN, __llvm_libc::atanhf(aNaN)); + EXPECT_FP_EXCEPTION(FE_INVALID); + EXPECT_MATH_ERRNO(0); + + __llvm_libc::fputil::clear_except(FE_ALL_EXCEPT); + EXPECT_FP_EQ(0.0f, __llvm_libc::atanhf(0.0f)); + EXPECT_FP_EXCEPTION(0); + EXPECT_MATH_ERRNO(0); + + __llvm_libc::fputil::clear_except(FE_ALL_EXCEPT); + EXPECT_FP_EQ(-0.0f, __llvm_libc::atanhf(-0.0f)); + EXPECT_FP_EXCEPTION(0); + EXPECT_MATH_ERRNO(0); + + __llvm_libc::fputil::clear_except(FE_ALL_EXCEPT); + EXPECT_FP_EQ(inf, __llvm_libc::atanhf(1.0f)); + EXPECT_FP_EXCEPTION(FE_DIVBYZERO); + EXPECT_MATH_ERRNO(ERANGE); + + __llvm_libc::fputil::clear_except(FE_ALL_EXCEPT); + EXPECT_FP_EQ(neg_inf, __llvm_libc::atanhf(-1.0f)); + EXPECT_FP_EXCEPTION(FE_DIVBYZERO); + EXPECT_MATH_ERRNO(ERANGE); + + auto bt = FPBits(1.0f); + bt.bits += 1; + + __llvm_libc::fputil::clear_except(FE_ALL_EXCEPT); + EXPECT_FP_EQ(aNaN, __llvm_libc::atanhf(bt.get_val())); + EXPECT_FP_EXCEPTION(FE_INVALID); + EXPECT_MATH_ERRNO(EDOM); + + __llvm_libc::fputil::clear_except(FE_ALL_EXCEPT); + bt.set_sign(true); + EXPECT_FP_EQ(aNaN, __llvm_libc::atanhf(bt.get_val())); + EXPECT_FP_EXCEPTION(FE_INVALID); + EXPECT_MATH_ERRNO(EDOM); + + __llvm_libc::fputil::clear_except(FE_ALL_EXCEPT); + EXPECT_FP_EQ(aNaN, __llvm_libc::atanhf(2.0f)); + EXPECT_FP_EXCEPTION(FE_INVALID); + EXPECT_MATH_ERRNO(EDOM); + + __llvm_libc::fputil::clear_except(FE_ALL_EXCEPT); + EXPECT_FP_EQ(aNaN, __llvm_libc::atanhf(-2.0f)); + EXPECT_FP_EXCEPTION(FE_INVALID); + EXPECT_MATH_ERRNO(EDOM); + + __llvm_libc::fputil::clear_except(FE_ALL_EXCEPT); + EXPECT_FP_EQ(aNaN, __llvm_libc::atanhf(inf)); + EXPECT_FP_EXCEPTION(FE_INVALID); + EXPECT_MATH_ERRNO(EDOM); + + bt.set_sign(true); + EXPECT_FP_EQ(aNaN, __llvm_libc::atanhf(neg_inf)); + EXPECT_FP_EXCEPTION(FE_INVALID); + EXPECT_MATH_ERRNO(EDOM); +} + +TEST(LlvmLibcAtanhfTest, InFloatRange) { + constexpr uint32_t COUNT = 1000000; + const uint32_t STEP = FPBits(1.0f).uintval() / COUNT; + for (uint32_t i = 0, v = 0; i <= COUNT; ++i, v += STEP) { + float x = float(FPBits(v)); + ASSERT_MPFR_MATCH(mpfr::Operation::Atanh, x, __llvm_libc::atanhf(x), 0.5); + ASSERT_MPFR_MATCH(mpfr::Operation::Atanh, -x, __llvm_libc::atanhf(-x), 0.5); + } +} + +// For small values, tanh(x) is x. +TEST(LlvmLibcAtanhfTest, SmallValues) { + float x = float(FPBits(uint32_t(0x17800000))); + float result = __llvm_libc::atanhf(x); + EXPECT_MPFR_MATCH(mpfr::Operation::Atanh, x, result, 0.5); + EXPECT_FP_EQ(x, result); + + x = float(FPBits(uint32_t(0x00400000))); + result = __llvm_libc::atanhf(x); + EXPECT_MPFR_MATCH(mpfr::Operation::Atanh, x, result, 0.5); + EXPECT_FP_EQ(x, result); +} diff --git a/libc/test/src/math/exhaustive/CMakeLists.txt b/libc/test/src/math/exhaustive/CMakeLists.txt --- a/libc/test/src/math/exhaustive/CMakeLists.txt +++ b/libc/test/src/math/exhaustive/CMakeLists.txt @@ -291,3 +291,37 @@ -lpthread ) +add_fp_unittest( + atanhf_test + NO_RUN_POSTBUILD + NEED_MPFR + SUITE + libc_math_exhaustive_tests + SRCS + atanhf_test.cpp + DEPENDS + .exhaustive_test + libc.include.math + libc.src.math.atanhf + libc.src.__support.FPUtil.fputil + LINK_LIBRARIES + -lpthread +) + +add_fp_unittest( + atanf_test + NO_RUN_POSTBUILD + NEED_MPFR + SUITE + libc_math_exhaustive_tests + SRCS + atanf_test.cpp + DEPENDS + .exhaustive_test + libc.include.math + libc.src.math.atanf + libc.src.__support.FPUtil.fputil + LINK_LIBRARIES + -lpthread +) + diff --git a/libc/test/src/math/exhaustive/atanf_test.cpp b/libc/test/src/math/exhaustive/atanf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/exhaustive/atanf_test.cpp @@ -0,0 +1,76 @@ +//===-- Exhaustive test for atanf -----------------------------------------===// +// +// 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/__support/FPUtil/FPBits.h" +#include "src/math/atanf.h" +#include "utils/MPFRWrapper/MPFRUtils.h" + +#include + +using FPBits = __llvm_libc::fputil::FPBits; + +namespace mpfr = __llvm_libc::testing::mpfr; + +struct LlvmLibcAtanfExhaustiveTest : public LlvmLibcExhaustiveTest { + bool check(uint32_t start, uint32_t stop, + mpfr::RoundingMode rounding) override { + mpfr::ForceRoundingMode r(rounding); + uint32_t bits = start; + bool result = true; + do { + FPBits xbits(bits); + float x = float(xbits); + result &= EXPECT_MPFR_MATCH(mpfr::Operation::Atan, x, + __llvm_libc::atanf(x), 0.5, rounding); + } while (bits++ < stop); + return result; + } +}; + +static const int NUM_THREADS = std::thread::hardware_concurrency(); + +// Range: [0, 1.0]; +static const uint32_t POS_START = 0x0000'0000U; +static const uint32_t POS_STOP = FPBits::inf().uintval(); + +TEST_F(LlvmLibcAtanfExhaustiveTest, PostiveRangeRoundNearestTieToEven) { + test_full_range(POS_START, POS_STOP, mpfr::RoundingMode::Nearest); +} + +TEST_F(LlvmLibcAtanfExhaustiveTest, PostiveRangeRoundUp) { + test_full_range(POS_START, POS_STOP, mpfr::RoundingMode::Upward); +} + +TEST_F(LlvmLibcAtanfExhaustiveTest, PostiveRangeRoundDown) { + test_full_range(POS_START, POS_STOP, mpfr::RoundingMode::Downward); +} + +TEST_F(LlvmLibcAtanfExhaustiveTest, PostiveRangeRoundTowardZero) { + test_full_range(POS_START, POS_STOP, mpfr::RoundingMode::TowardZero); +} + +// Range: [-1.0, 0]; +static const uint32_t NEG_START = 0x8000'0000U; +static const uint32_t NEG_STOP = FPBits::neg_inf().uintval(); + +TEST_F(LlvmLibcAtanfExhaustiveTest, NegativeRangeRoundNearestTieToEven) { + test_full_range(NEG_START, NEG_STOP, mpfr::RoundingMode::Nearest); +} + +TEST_F(LlvmLibcAtanfExhaustiveTest, NegativeRangeRoundUp) { + test_full_range(NEG_START, NEG_STOP, mpfr::RoundingMode::Upward); +} + +TEST_F(LlvmLibcAtanfExhaustiveTest, NegativeRangeRoundDown) { + test_full_range(NEG_START, NEG_STOP, mpfr::RoundingMode::Downward); +} + +TEST_F(LlvmLibcAtanfExhaustiveTest, NegativeRangeRoundTowardZero) { + test_full_range(NEG_START, NEG_STOP, mpfr::RoundingMode::TowardZero); +} diff --git a/libc/test/src/math/exhaustive/atanhf_test.cpp b/libc/test/src/math/exhaustive/atanhf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/exhaustive/atanhf_test.cpp @@ -0,0 +1,76 @@ +//===-- Exhaustive test for atanhf ----------------------------------------===// +// +// 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/__support/FPUtil/FPBits.h" +#include "src/math/atanhf.h" +#include "utils/MPFRWrapper/MPFRUtils.h" + +#include + +using FPBits = __llvm_libc::fputil::FPBits; + +namespace mpfr = __llvm_libc::testing::mpfr; + +struct LlvmLibcAtanhfExhaustiveTest : public LlvmLibcExhaustiveTest { + bool check(uint32_t start, uint32_t stop, + mpfr::RoundingMode rounding) override { + mpfr::ForceRoundingMode r(rounding); + uint32_t bits = start; + bool result = true; + do { + FPBits xbits(bits); + float x = float(xbits); + result &= EXPECT_MPFR_MATCH(mpfr::Operation::Atanh, x, + __llvm_libc::atanhf(x), 0.5, rounding); + } while (bits++ < stop); + return result; + } +}; + +static const int NUM_THREADS = std::thread::hardware_concurrency(); + +// Range: [0, 1.0]; +static const uint32_t POS_START = 0x0000'0000U; +static const uint32_t POS_STOP = FPBits(1.0f).uintval(); + +TEST_F(LlvmLibcAtanhfExhaustiveTest, PostiveRangeRoundNearestTieToEven) { + test_full_range(POS_START, POS_STOP, mpfr::RoundingMode::Nearest); +} + +TEST_F(LlvmLibcAtanhfExhaustiveTest, PostiveRangeRoundUp) { + test_full_range(POS_START, POS_STOP, mpfr::RoundingMode::Upward); +} + +TEST_F(LlvmLibcAtanhfExhaustiveTest, PostiveRangeRoundDown) { + test_full_range(POS_START, POS_STOP, mpfr::RoundingMode::Downward); +} + +TEST_F(LlvmLibcAtanhfExhaustiveTest, PostiveRangeRoundTowardZero) { + test_full_range(POS_START, POS_STOP, mpfr::RoundingMode::TowardZero); +} + +// Range: [-1.0, 0]; +static const uint32_t NEG_START = 0x8000'0000U; +static const uint32_t NEG_STOP = FPBits(-1.0f).uintval(); + +TEST_F(LlvmLibcAtanhfExhaustiveTest, NegativeRangeRoundNearestTieToEven) { + test_full_range(NEG_START, NEG_STOP, mpfr::RoundingMode::Nearest); +} + +TEST_F(LlvmLibcAtanhfExhaustiveTest, NegativeRangeRoundUp) { + test_full_range(NEG_START, NEG_STOP, mpfr::RoundingMode::Upward); +} + +TEST_F(LlvmLibcAtanhfExhaustiveTest, NegativeRangeRoundDown) { + test_full_range(NEG_START, NEG_STOP, mpfr::RoundingMode::Downward); +} + +TEST_F(LlvmLibcAtanhfExhaustiveTest, NegativeRangeRoundTowardZero) { + test_full_range(NEG_START, NEG_STOP, mpfr::RoundingMode::TowardZero); +} diff --git a/libc/test/src/math/supfuncf_test.cpp b/libc/test/src/math/supfuncf_test.cpp --- a/libc/test/src/math/supfuncf_test.cpp +++ b/libc/test/src/math/supfuncf_test.cpp @@ -31,9 +31,10 @@ if (v > ustop) \ break; \ float x = FPBits(uint32_t(v)).get_val(); \ - if ((f_check)(x)) \ + if ((f_check)(x)) { \ EXPECT_MPFR_MATCH_ALL_ROUNDING(mfp_op, x, static_cast((f)(x)), \ (prec)); \ + } \ } \ } @@ -69,3 +70,13 @@ CHECK_DATA(0.0f, inf, mpfr::Operation::Log, __llvm_libc::log_eval, isnormal, def_count, def_prec); } + +TEST(LlvmLibcAtanfPosTest, InFloatRange) { + CHECK_DATA(0.0f, inf, mpfr::Operation::Atan, __llvm_libc::atan_eval, isfinite, + def_count, def_prec); +} + +TEST(LlvmLibcAtanfNegTest, InFloatRange) { + CHECK_DATA(-0.0f, neg_inf, mpfr::Operation::Atan, __llvm_libc::atan_eval, + isfinite, def_count, def_prec); +} diff --git a/libc/utils/MPFRWrapper/MPFRUtils.h b/libc/utils/MPFRWrapper/MPFRUtils.h --- a/libc/utils/MPFRWrapper/MPFRUtils.h +++ b/libc/utils/MPFRWrapper/MPFRUtils.h @@ -25,6 +25,8 @@ // and output floating point numbers are of the same kind. BeginUnaryOperationsSingleOutput, Abs, + Atan, + Atanh, Ceil, Cos, Cosh, diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp --- a/libc/utils/MPFRWrapper/MPFRUtils.cpp +++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp @@ -182,6 +182,18 @@ return result; } + MPFRNumber atan() const { + MPFRNumber result(*this); + mpfr_atan(result.value, value, mpfr_rounding); + return result; + } + + MPFRNumber atanh() const { + MPFRNumber result(*this); + mpfr_atanh(result.value, value, mpfr_rounding); + return result; + } + MPFRNumber ceil() const { MPFRNumber result(*this); mpfr_ceil(result.value, value); @@ -500,6 +512,10 @@ switch (op) { case Operation::Abs: return mpfrInput.abs(); + case Operation::Atan: + return mpfrInput.atan(); + case Operation::Atanh: + return mpfrInput.atanh(); case Operation::Ceil: return mpfrInput.ceil(); case Operation::Cos: