[libc-commits] [libc] [libc][math][c23] implement C23 math function `tanpif` (PR #147192)
Connector Switch via libc-commits
libc-commits at lists.llvm.org
Sun Jul 6 07:16:50 PDT 2025
https://github.com/c8ef created https://github.com/llvm/llvm-project/pull/147192
The smoke test and exhaustive test pass on x86_64 Linux.
>From 8ef8dd4c801fd4abbec67a8b2ad68641826e3fdf Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
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<float, N_EXCEPTS> 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<float>;
+ FPBits xbits(x);
+
+ uint32_t x_u = xbits.uintval();
+ uint32_t x_abs = x_u & 0x7fff'ffffU;
+ double xd = static_cast<double>(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<int32_t>(x - 0.5);
+ return ((x_mp5_u & 0x1) ? -1 : 1) * FPBits::inf().get_val();
+ }
+
+ using fputil::multiply_add;
+ return fputil::cast<float>(
+ 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<float, mpfr::Operation::Tanpi,
+ LIBC_NAMESPACE::tanpif>;
+
+// 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<float>;
+
+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);
+}
More information about the libc-commits
mailing list