[libc-commits] [libc] [llvm] [libc][math][c23] Fix signed zeros in `tanpif` and `tanpif16` (PR #230371)
Connector Switch via libc-commits
libc-commits at lists.llvm.org
Sat Oct 17 23:38:02 PDT 2026
https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/230371
>From c91dd1473d510ade5c116e5ec143c511c950c119 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 9 Oct 2026 14:28:03 +0800
Subject: [PATCH 1/2] Fix signed zeros in tanpif and tanpif16
---
libc/src/__support/math/CMakeLists.txt | 15 ++++-
libc/src/__support/math/tanpi_utils.h | 59 +++++++++++++++++++
libc/src/__support/math/tanpif.h | 57 ++++++++++--------
libc/src/__support/math/tanpif16.h | 53 +++++++++--------
libc/test/src/math/tanpif16_test.cpp | 16 +++++
libc/test/src/math/tanpif_test.cpp | 16 +++++
.../llvm-project-overlay/libc/BUILD.bazel | 13 ++++
7 files changed, 178 insertions(+), 51 deletions(-)
create mode 100644 libc/src/__support/math/tanpi_utils.h
diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt
index ef33e3f8ada7b0..b6cab10b85c548 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -5801,12 +5801,24 @@ add_header_library(
libc.include.llvm-libc-macros.float16_macros
)
+add_header_library(
+ tanpi_utils
+ HDRS
+ tanpi_utils.h
+ DEPENDS
+ libc.src.__support.CPP.bit
+ libc.src.__support.FPUtil.fp_bits
+ libc.src.__support.macros.attributes
+ libc.src.__support.macros.config
+)
+
add_header_library(
tanpif
HDRS
tanpif.h
DEPENDS
- .sincosf16_utils
+ .sincosf_utils
+ .tanpi_utils
libc.src.__support.FPUtil.fenv_impl
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.cast
@@ -5823,6 +5835,7 @@ add_header_library(
tanpif16.h
DEPENDS
.sincosf16_utils
+ .tanpi_utils
libc.hdr.errno_macros
libc.hdr.fenv_macros
libc.include.llvm-libc-macros.float16_macros
diff --git a/libc/src/__support/math/tanpi_utils.h b/libc/src/__support/math/tanpi_utils.h
new file mode 100644
index 00000000000000..ebf65541d0adee
--- /dev/null
+++ b/libc/src/__support/math/tanpi_utils.h
@@ -0,0 +1,59 @@
+//===-- Shared tanpi utilities --------------------------*- 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___SUPPORT_MATH_TANPI_UTILS_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_TANPI_UTILS_H
+
+#include "src/__support/CPP/bit.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace math {
+namespace tanpi_internal {
+
+// These helpers require finite inputs.
+
+// Check if x is an odd integer: its lowest set significand bit must be at the
+// unit position, so x_e + lsb == UNIT_EXPONENT.
+template <typename T> LIBC_INLINE constexpr bool is_odd_integer(T x) {
+ using FPBits = fputil::FPBits<T>;
+ using StorageType = typename FPBits::StorageType;
+ FPBits xbits(x);
+ StorageType x_u = xbits.uintval();
+ unsigned x_e = static_cast<unsigned>(xbits.get_biased_exponent());
+ unsigned lsb = static_cast<unsigned>(
+ cpp::countr_zero(static_cast<StorageType>(x_u | FPBits::EXP_MASK)));
+ constexpr unsigned UNIT_EXPONENT =
+ static_cast<unsigned>(FPBits::EXP_BIAS + FPBits::FRACTION_LEN);
+ return x_e + lsb == UNIT_EXPONENT;
+}
+
+// Check if x is an integer: its lowest set significand bit must be at or above
+// the unit position, so x_e + lsb >= UNIT_EXPONENT.
+template <typename T> LIBC_INLINE constexpr bool is_integer(T x) {
+ using FPBits = fputil::FPBits<T>;
+ using StorageType = typename FPBits::StorageType;
+ FPBits xbits(x);
+ if (xbits.is_zero())
+ return true;
+ StorageType x_u = xbits.uintval();
+ unsigned x_e = static_cast<unsigned>(xbits.get_biased_exponent());
+ unsigned lsb = static_cast<unsigned>(
+ cpp::countr_zero(static_cast<StorageType>(x_u | FPBits::EXP_MASK)));
+ constexpr unsigned UNIT_EXPONENT =
+ static_cast<unsigned>(FPBits::EXP_BIAS + FPBits::FRACTION_LEN);
+ return x_e + lsb >= UNIT_EXPONENT;
+}
+
+} // namespace tanpi_internal
+} // namespace math
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_TANPI_UTILS_H
diff --git a/libc/src/__support/math/tanpif.h b/libc/src/__support/math/tanpif.h
index 114fcb6053d30d..721a4b07cf63a3 100644
--- a/libc/src/__support/math/tanpif.h
+++ b/libc/src/__support/math/tanpif.h
@@ -18,6 +18,7 @@
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
+#include "tanpi_utils.h"
namespace LIBC_NAMESPACE_DECL {
@@ -25,6 +26,7 @@ namespace math {
LIBC_INLINE float tanpif(float x) {
using namespace sincosf_utils_internal;
+ using namespace tanpi_internal;
using FPBits = typename fputil::FPBits<float>;
FPBits xbits(x);
@@ -41,43 +43,45 @@ LIBC_INLINE float tanpif(float 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;
+ // Handle NaN and Inf.
+ 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();
+ }
+
+ // Handle signed zeros.
+ if (LIBC_UNLIKELY(x_abs == 0U))
+ return x;
+
+ if (LIBC_UNLIKELY(is_integer(x))) {
+ // Preserve the input sign for even integers and flip it for odd integers.
+ Sign sign = is_odd_integer(x) ? xbits.sign().negate() : xbits.sign();
+ return FPBits::zero(sign).get_val();
+ }
#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+ // Handle exceptional values.
+ if (LIBC_UNLIKELY(x_abs <= 0x3F3663FF)) {
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();
}
+#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
// Range reduction:
+ // All remaining inputs are finite nonintegers, so |x| < 2^23.
// For |x| > 1/32, we perform range reduction as follows:
// Find k and y such that:
// x = (k + y) * 1/32
@@ -91,6 +95,7 @@ LIBC_INLINE float tanpif(float x) {
// 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 xd = static_cast<double>(x);
double sin_k, cos_k, sin_y, cosm1_y;
sincospif_eval(xd, sin_k, cos_k, sin_y, cosm1_y);
diff --git a/libc/src/__support/math/tanpif16.h b/libc/src/__support/math/tanpif16.h
index dff6b518cc95b6..1a122ad399f3e0 100644
--- a/libc/src/__support/math/tanpif16.h
+++ b/libc/src/__support/math/tanpif16.h
@@ -22,24 +22,47 @@
#include "src/__support/FPUtil/except_value_utils.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/macros/optimization.h"
+#include "tanpi_utils.h"
namespace LIBC_NAMESPACE_DECL {
namespace math {
LIBC_INLINE float16 tanpif16(float16 x) {
using namespace sincosf16_internal;
+ using namespace tanpi_internal;
using FPBits = typename fputil::FPBits<float16>;
FPBits xbits(x);
uint16_t x_u = xbits.uintval();
uint16_t x_abs = x_u & 0x7fff;
- // Handle exceptional values
- if (LIBC_UNLIKELY(x_abs <= 0x4335)) {
- if (LIBC_UNLIKELY(x_abs == 0U))
- return x;
+ // Handle NaN and Inf.
+ if (LIBC_UNLIKELY(x_abs >= 0x7c00)) {
+ if (xbits.is_signaling_nan()) {
+ fputil::raise_except_if_required(FE_INVALID);
+ return FPBits::quiet_nan().get_val();
+ }
+ if (x_abs == 0x7c00) {
+ fputil::set_errno_if_required(EDOM);
+ fputil::raise_except_if_required(FE_INVALID);
+ }
+
+ return x + FPBits::quiet_nan().get_val();
+ }
+
+ // Handle signed zeros.
+ if (LIBC_UNLIKELY(x_abs == 0U))
+ return x;
+
+ if (LIBC_UNLIKELY(is_integer(x))) {
+ // Preserve the input sign for even integers and flip it for odd integers.
+ Sign sign = is_odd_integer(x) ? xbits.sign().negate() : xbits.sign();
+ return FPBits::zero(sign).get_val();
+ }
#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+ // Handle exceptional values.
+ if (LIBC_UNLIKELY(x_abs <= 0x4335)) {
constexpr size_t N_EXCEPTS = 21;
constexpr fputil::ExceptValues<float16, N_EXCEPTS> TANPIF16_EXCEPTS{{
@@ -62,29 +85,11 @@ LIBC_INLINE float16 tanpif16(float16 x) {
if (auto r = TANPIF16_EXCEPTS.lookup_odd(x_abs, x_sign);
LIBC_UNLIKELY(r.has_value()))
return r.value();
-#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
}
+#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
- // Numbers greater or equal to 2^10 are integers, or infinity, or NaN
- if (LIBC_UNLIKELY(x_abs >= 0x6400)) {
- // Check for NaN or infinity values
- if (LIBC_UNLIKELY(x_abs >= 0x7c00)) {
- if (xbits.is_signaling_nan()) {
- fputil::raise_except_if_required(FE_INVALID);
- return FPBits::quiet_nan().get_val();
- }
- // is inf
- if (x_abs == 0x7c00) {
- 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:
+ // All remaining inputs are finite nonintegers, so |x| < 2^10.
// For |x| > 1/32, we perform range reduction as follows:
// Find k and y such that:
// x = (k + y) * 1/32
diff --git a/libc/test/src/math/tanpif16_test.cpp b/libc/test/src/math/tanpif16_test.cpp
index f994781dd84b1a..e7f1338074f0e0 100644
--- a/libc/test/src/math/tanpif16_test.cpp
+++ b/libc/test/src/math/tanpif16_test.cpp
@@ -47,3 +47,19 @@ TEST_F(LlvmLibcTanpif16Test, NegativeRange) {
TOLERANCE + 0.5);
}
}
+
+TEST_F(LlvmLibcTanpif16Test, IntegerSigns) {
+ // The MPFR matcher does not distinguish the signs of zeros.
+ constexpr struct {
+ float16 input, expected;
+ } ZERO_TESTS[] = {{1.0f16, neg_zero},
+ {2.0f16, zero},
+ {0x1.004p10f16, neg_zero}, // 2^10 + 1 is odd.
+ {0x1.004p11f16, zero}}; // 2^11 + 2 is even.
+ for (const auto &test : ZERO_TESTS) {
+ EXPECT_FP_EQ_ALL_ROUNDING(test.expected,
+ LIBC_NAMESPACE::tanpif16(test.input));
+ EXPECT_FP_EQ_ALL_ROUNDING(-test.expected,
+ LIBC_NAMESPACE::tanpif16(-test.input));
+ }
+}
diff --git a/libc/test/src/math/tanpif_test.cpp b/libc/test/src/math/tanpif_test.cpp
index 47f794c4e1b37c..721d7c1c6aac34 100644
--- a/libc/test/src/math/tanpif_test.cpp
+++ b/libc/test/src/math/tanpif_test.cpp
@@ -89,3 +89,19 @@ TEST_F(LlvmLibcTanpifTest, SDCOMP_26094) {
LIBC_NAMESPACE::tanpif(x), 0.5);
}
}
+
+TEST_F(LlvmLibcTanpifTest, IntegerSigns) {
+ // The MPFR matcher does not distinguish the signs of zeros.
+ constexpr struct {
+ float input, expected;
+ } ZERO_TESTS[] = {{1.0f, neg_zero},
+ {2.0f, zero},
+ {0x1.000002p23f, neg_zero}, // 2^23 + 1 is odd.
+ {0x1.000002p24f, zero}}; // 2^24 + 2 is even.
+ for (const auto &test : ZERO_TESTS) {
+ EXPECT_FP_EQ_ALL_ROUNDING(test.expected,
+ LIBC_NAMESPACE::tanpif(test.input));
+ EXPECT_FP_EQ_ALL_ROUNDING(-test.expected,
+ LIBC_NAMESPACE::tanpif(-test.input));
+ }
+}
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index cbe1c92e9e6be8..b46d843cc8abe9 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -11307,6 +11307,17 @@ libc_support_library(
],
)
+libc_support_library(
+ name = "__support_math_tanpi_utils",
+ hdrs = ["src/__support/math/tanpi_utils.h"],
+ deps = [
+ ":__support_cpp_bit",
+ ":__support_fputil_fp_bits",
+ ":__support_macros_attributes",
+ ":__support_macros_config",
+ ],
+)
+
libc_support_library(
name = "__support_math_tanpif16",
hdrs = ["src/__support/math/tanpif16.h"],
@@ -11318,6 +11329,7 @@ libc_support_library(
":__support_fputil_multiply_add",
":__support_macros_optimization",
":__support_math_sincosf16_utils",
+ ":__support_math_tanpi_utils",
":hdr_errno_macros",
":hdr_fenv_macros",
":llvm_libc_macros_float16_macros",
@@ -11661,6 +11673,7 @@ libc_support_library(
":__support_fputil_multiply_add",
":__support_macros_config",
":__support_macros_optimization",
+ ":__support_math_tanpi_utils",
":__support_sincosf_utils",
],
)
>From a33510f8383eb70fc42441f3ce327781f3db203d Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sun, 18 Oct 2026 14:37:46 +0800
Subject: [PATCH 2/2] make tanpi_utils.h include relative to libc/
---
libc/src/__support/math/tanpif.h | 2 +-
libc/src/__support/math/tanpif16.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/libc/src/__support/math/tanpif.h b/libc/src/__support/math/tanpif.h
index 721a4b07cf63a3..563a2b24172c5c 100644
--- a/libc/src/__support/math/tanpif.h
+++ b/libc/src/__support/math/tanpif.h
@@ -18,7 +18,7 @@
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
-#include "tanpi_utils.h"
+#include "src/__support/math/tanpi_utils.h"
namespace LIBC_NAMESPACE_DECL {
diff --git a/libc/src/__support/math/tanpif16.h b/libc/src/__support/math/tanpif16.h
index 1a122ad399f3e0..d4950cf834616d 100644
--- a/libc/src/__support/math/tanpif16.h
+++ b/libc/src/__support/math/tanpif16.h
@@ -22,7 +22,7 @@
#include "src/__support/FPUtil/except_value_utils.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/macros/optimization.h"
-#include "tanpi_utils.h"
+#include "src/__support/math/tanpi_utils.h"
namespace LIBC_NAMESPACE_DECL {
namespace math {
More information about the libc-commits
mailing list