From 8ef8dd4c801fd4abbec67a8b2ad68641826e3fdf Mon Sep 17 00:00:00 2001 From: c8ef Date: Sun, 6 Jul 2025 22:14:06 +0800 Subject: [PATCH] implement tanpif --- libc/config/linux/aarch64/entrypoints.txt | 1 + libc/config/linux/x86_64/entrypoints.txt | 1 + libc/docs/headers/math/index.rst | 2 +- libc/include/math.yaml | 6 + libc/src/math/CMakeLists.txt | 2 + libc/src/math/generic/CMakeLists.txt | 15 +++ libc/src/math/generic/tanpif.cpp | 106 ++++++++++++++++++ libc/src/math/tanpif.h | 20 ++++ libc/test/src/math/exhaustive/CMakeLists.txt | 16 +++ libc/test/src/math/exhaustive/tanpif_test.cpp | 33 ++++++ libc/test/src/math/smoke/CMakeLists.txt | 11 ++ libc/test/src/math/smoke/tanpif_test.cpp | 36 ++++++ 12 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 libc/src/math/generic/tanpif.cpp create mode 100644 libc/src/math/tanpif.h create mode 100644 libc/test/src/math/exhaustive/tanpif_test.cpp create mode 100644 libc/test/src/math/smoke/tanpif_test.cpp diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 9e042cd4a8acb..cff5b7f8312d6 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -625,6 +625,7 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.tan libc.src.math.tanf libc.src.math.tanhf + libc.src.math.tanpif libc.src.math.totalorder libc.src.math.totalorderf libc.src.math.totalorderl diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 59c248871f83a..ea31d858dbc44 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -657,6 +657,7 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.tan libc.src.math.tanf libc.src.math.tanhf + libc.src.math.tanpif libc.src.math.totalorder libc.src.math.totalorderf libc.src.math.totalorderl diff --git a/libc/docs/headers/math/index.rst b/libc/docs/headers/math/index.rst index 3cb41a6871b94..9679c4a6c807f 100644 --- a/libc/docs/headers/math/index.rst +++ b/libc/docs/headers/math/index.rst @@ -349,7 +349,7 @@ Higher Math Functions +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ | tanh | |check| | | | |check| | | 7.12.5.6 | F.10.2.6 | +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ -| tanpi | | | | |check| | | 7.12.4.14 | F.10.1.14 | +| tanpi | |check| | | | |check| | | 7.12.4.14 | F.10.1.14 | +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ | tgamma | | | | | | 7.12.8.4 | F.10.5.4 | +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+ diff --git a/libc/include/math.yaml b/libc/include/math.yaml index 11bead0745954..3044ec3437ff8 100644 --- a/libc/include/math.yaml +++ b/libc/include/math.yaml @@ -2524,6 +2524,12 @@ functions: arguments: - type: _Float16 guard: LIBC_TYPES_HAS_FLOAT16 + - name: tanpif + standards: + - stdc + return_type: float + arguments: + - type: float - name: tanpif16 standards: - stdc diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt index b27b0d2b523f8..455ad3456573a 100644 --- a/libc/src/math/CMakeLists.txt +++ b/libc/src/math/CMakeLists.txt @@ -519,6 +519,8 @@ add_math_entrypoint_object(tanf16) add_math_entrypoint_object(tanh) add_math_entrypoint_object(tanhf) add_math_entrypoint_object(tanhf16) + +add_math_entrypoint_object(tanpif) add_math_entrypoint_object(tanpif16) add_math_entrypoint_object(tgamma) diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt index fd1e6c0d648aa..a379110853012 100644 --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -606,6 +606,21 @@ add_entrypoint_object( libc.src.__support.macros.properties.types ) +add_entrypoint_object( + tanpif + SRCS + tanpif.cpp + HDRS + ../tanpif.h + DEPENDS + .sincosf_utils + libc.src.__support.FPUtil.except_value_utils + libc.src.__support.FPUtil.fenv_impl + libc.src.__support.FPUtil.fp_bits + libc.src.__support.FPUtil.multiply_add + libc.src.__support.macros.optimization +) + add_entrypoint_object( tanpif16 SRCS diff --git a/libc/src/math/generic/tanpif.cpp b/libc/src/math/generic/tanpif.cpp new file mode 100644 index 0000000000000..ff1984d325d91 --- /dev/null +++ b/libc/src/math/generic/tanpif.cpp @@ -0,0 +1,106 @@ +//===-- Single-precision tanpi 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/tanpif.h" +#include "sincosf_utils.h" +#include "src/__support/FPUtil/FEnvImpl.h" +#include "src/__support/FPUtil/FPBits.h" +#include "src/__support/FPUtil/cast.h" +#include "src/__support/FPUtil/except_value_utils.h" +#include "src/__support/FPUtil/multiply_add.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY + +namespace LIBC_NAMESPACE_DECL { + +#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS +constexpr size_t N_EXCEPTS = 3; + +constexpr fputil::ExceptValues TANPIF_EXCEPTS{{ + // (input, RZ output, RU offset, RD offset, RN offset) + {0x38F26685, 0x39BE6182, 1, 0, 0}, + {0x3E933802, 0x3FA267DD, 1, 0, 0}, + {0x3F3663FF, 0xBFA267DD, 0, 1, 0}, +}}; +#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS + +LLVM_LIBC_FUNCTION(float, tanpif, (float x)) { + using FPBits = typename fputil::FPBits; + FPBits xbits(x); + + uint32_t x_u = xbits.uintval(); + uint32_t x_abs = x_u & 0x7fff'ffffU; + double xd = static_cast(xbits.get_val()); + + // Handle exceptional values + if (LIBC_UNLIKELY(x_abs <= 0x3F3663FF)) { + if (LIBC_UNLIKELY(x_abs == 0U)) + return x; + +#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS + bool x_sign = x_u >> 31; + + if (auto r = TANPIF_EXCEPTS.lookup_odd(x_abs, x_sign); + LIBC_UNLIKELY(r.has_value())) + return r.value(); +#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS + } + + // Numbers greater or equal to 2^23 are always integers, or infinity, or NaN + if (LIBC_UNLIKELY(x_abs >= 0x4B00'0000)) { + // x is inf or NaN. + if (LIBC_UNLIKELY(x_abs >= 0x7f80'0000U)) { + if (xbits.is_signaling_nan()) { + fputil::raise_except_if_required(FE_INVALID); + return FPBits::quiet_nan().get_val(); + } + + if (x_abs == 0x7f80'0000U) { + fputil::set_errno_if_required(EDOM); + fputil::raise_except_if_required(FE_INVALID); + } + + return x + FPBits::quiet_nan().get_val(); + } + + return FPBits::zero(xbits.sign()).get_val(); + } + + // Range reduction: + // For |x| > 1/32, we perform range reduction as follows: + // Find k and y such that: + // x = (k + y) * 1/32 + // k is an integer + // |y| < 0.5 + // + // This is done by performing: + // k = round(x * 32) + // y = x * 32 - k + // + // Once k and y are computed, we then deduce the answer by the formula: + // tan(x) = sin(x) / cos(x) + // = (sin_y * cos_k + cos_y * sin_k) / (cos_y * cos_k - sin_y * sin_k) + double sin_k, cos_k, sin_y, cosm1_y; + sincospif_eval(xd, sin_k, cos_k, sin_y, cosm1_y); + + if (LIBC_UNLIKELY(sin_y == 0 && cos_k == 0)) { + fputil::set_errno_if_required(EDOM); + fputil::raise_except_if_required(FE_DIVBYZERO); + + int32_t x_mp5_u = static_cast(x - 0.5); + return ((x_mp5_u & 0x1) ? -1 : 1) * FPBits::inf().get_val(); + } + + using fputil::multiply_add; + return fputil::cast( + multiply_add(sin_y, cos_k, multiply_add(cosm1_y, sin_k, sin_k)) / + multiply_add(sin_y, -sin_k, multiply_add(cosm1_y, cos_k, cos_k))); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/math/tanpif.h b/libc/src/math/tanpif.h new file mode 100644 index 0000000000000..59e6dcfa9ff73 --- /dev/null +++ b/libc/src/math/tanpif.h @@ -0,0 +1,20 @@ +//===-- Implementation header for tanpif ------------------------*- 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_TANPIF_H +#define LLVM_LIBC_SRC_MATH_TANPIF_H + +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +float tanpif(float x); + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_MATH_TANPIF_H diff --git a/libc/test/src/math/exhaustive/CMakeLists.txt b/libc/test/src/math/exhaustive/CMakeLists.txt index 551f449c9c8db..1a23a335b2fff 100644 --- a/libc/test/src/math/exhaustive/CMakeLists.txt +++ b/libc/test/src/math/exhaustive/CMakeLists.txt @@ -122,6 +122,22 @@ add_fp_unittest( -lpthread ) +add_fp_unittest( + tanpif_test + NO_RUN_POSTBUILD + NEED_MPFR + SUITE + libc_math_exhaustive_tests + SRCS + tanpif_test.cpp + DEPENDS + .exhaustive_test + libc.src.math.tanpif + libc.src.__support.FPUtil.fp_bits + LINK_LIBRARIES + -lpthread +) + add_fp_unittest( erff_test NO_RUN_POSTBUILD diff --git a/libc/test/src/math/exhaustive/tanpif_test.cpp b/libc/test/src/math/exhaustive/tanpif_test.cpp new file mode 100644 index 0000000000000..44a8a06f52374 --- /dev/null +++ b/libc/test/src/math/exhaustive/tanpif_test.cpp @@ -0,0 +1,33 @@ +//===-- Exhaustive test for tanpif ----------------------------------------===// +// +// 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/tanpif.h" +#include "utils/MPFRWrapper/MPFRUtils.h" + +namespace mpfr = LIBC_NAMESPACE::testing::mpfr; + +using LlvmLibcTanpifExhaustiveTest = + LlvmLibcUnaryOpExhaustiveMathTest; + +// Range: [0, Inf]; +static constexpr uint32_t POS_START = 0x0000'0000U; +static constexpr uint32_t POS_STOP = 0x7f80'0000U; + +TEST_F(LlvmLibcTanpifExhaustiveTest, PostiveRange) { + test_full_range_all_roundings(POS_START, POS_STOP); +} + +// Range: [-Inf, 0]; +static constexpr uint32_t NEG_START = 0xb000'0000U; +static constexpr uint32_t NEG_STOP = 0xff80'0000U; + +TEST_F(LlvmLibcTanpifExhaustiveTest, NegativeRange) { + test_full_range_all_roundings(NEG_START, NEG_STOP); +} diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt index 599939cbec4b5..4aafe03d1d08b 100644 --- a/libc/test/src/math/smoke/CMakeLists.txt +++ b/libc/test/src/math/smoke/CMakeLists.txt @@ -134,6 +134,17 @@ add_fp_unittest( libc.src.math.tanf16 ) +add_fp_unittest( + tanpif_test + SUITE + libc-math-smoke-tests + SRCS + tanpif_test.cpp + DEPENDS + libc.src.errno.errno + libc.src.math.tanpif +) + add_fp_unittest( tanpif16_test SUITE diff --git a/libc/test/src/math/smoke/tanpif_test.cpp b/libc/test/src/math/smoke/tanpif_test.cpp new file mode 100644 index 0000000000000..e122f57a2fe0f --- /dev/null +++ b/libc/test/src/math/smoke/tanpif_test.cpp @@ -0,0 +1,36 @@ +//===-- Unittests for tanpif ----------------------------------------------===// +// +// 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/libc_errno.h" +#include "src/math/tanpif.h" +#include "test/UnitTest/FPMatcher.h" +#include "test/UnitTest/Test.h" + +using LlvmLibcTanpifTest = LIBC_NAMESPACE::testing::FPTest; + +TEST_F(LlvmLibcTanpifTest, SpecialNumbers) { + libc_errno = 0; + + EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::tanpif(sNaN), FE_INVALID); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::tanpif(aNaN)); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_EQ(zero, LIBC_NAMESPACE::tanpif(zero)); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_EQ(neg_zero, LIBC_NAMESPACE::tanpif(neg_zero)); + EXPECT_MATH_ERRNO(0); + + EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::tanpif(inf)); + EXPECT_MATH_ERRNO(EDOM); + + EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::tanpif(neg_inf)); + EXPECT_MATH_ERRNO(EDOM); +}