[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