[libc-commits] [libc] [libc][math] Fix `tanpif` and `tanpif16` error handling (PR #235296)

Connector Switch via libc-commits libc-commits at lists.llvm.org
Sat Oct 24 06:40:59 PDT 2026


https://github.com/c8ef created https://github.com/llvm/llvm-project/pull/235296

Relates to https://github.com/llvm/llvm-project/pull/147192#issuecomment-5726874002.

- Set ERANGE when tanpif and tanpif16 underflow.
- Set ERANGE instead of EDOM at half-integer poles.
- Raise underflow and inexact exceptions explicitly in tanpif16.
- Return exact results for odd multiples of one quarter without raising inexact.

Tested both functions with CORE-MATH's exhaustive checker in all four rounding modes. With `CORE_MATH_SUPPORT_ERRNO` and `CORE_MATH_CHECK_INEXACT` enabled.

>From 83ccd995a232988de8da24fe732ba692239d2239 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Sat, 24 Oct 2026 21:34:29 +0800
Subject: [PATCH] [libc][math] Fix tanpif and tanpif16 error handling

---
 libc/src/__support/math/tanpif.h           | 12 ++++++++++--
 libc/src/__support/math/tanpif16.h         | 14 ++++++++++++--
 libc/test/src/math/smoke/tanpif16_test.cpp | 12 ++++++++++++
 libc/test/src/math/smoke/tanpif_test.cpp   | 11 +++++++++++
 4 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/libc/src/__support/math/tanpif.h b/libc/src/__support/math/tanpif.h
index 99eec84c86613c..df39c29679b196 100644
--- a/libc/src/__support/math/tanpif.h
+++ b/libc/src/__support/math/tanpif.h
@@ -62,6 +62,10 @@ LIBC_INLINE float tanpif(float x) {
   if (LIBC_UNLIKELY(x_abs == 0U))
     return x;
 
+  // tanpi(x) underflows for 0 < |x| <= 0x1.45f3p-128.
+  if (LIBC_UNLIKELY(x_abs <= 0x0028'be60U))
+    fputil::set_errno_if_required(ERANGE);
+
   if (LIBC_UNLIKELY(fputil::is_integer(x))) {
     // Preserve the input sign for even integers and flip it for odd integers.
     Sign sign =
@@ -69,6 +73,11 @@ LIBC_INLINE float tanpif(float x) {
     return FPBits::zero(sign).get_val();
   }
 
+  // Odd multiples of 1/4 have exact results of +1 or -1.
+  double xd = static_cast<double>(x);
+  if (LIBC_UNLIKELY(fputil::is_odd_integer(4.0 * xd)))
+    return fputil::is_odd_integer(2.0 * xd - 0.5) ? -1.0f : 1.0f;
+
 #ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
   // Handle exceptional values.
   if (LIBC_UNLIKELY(x_abs <= 0x3F3663FF)) {
@@ -95,12 +104,11 @@ 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);
 
   if (LIBC_UNLIKELY(sin_y == 0 && cos_k == 0)) {
-    fputil::set_errno_if_required(EDOM);
+    fputil::set_errno_if_required(ERANGE);
     fputil::raise_except_if_required(FE_DIVBYZERO);
 
     int32_t x_mp5_i = static_cast<int32_t>(xd - 0.5);
diff --git a/libc/src/__support/math/tanpif16.h b/libc/src/__support/math/tanpif16.h
index bec8c17f2f0c0d..e13b4b5279c386 100644
--- a/libc/src/__support/math/tanpif16.h
+++ b/libc/src/__support/math/tanpif16.h
@@ -53,6 +53,12 @@ LIBC_INLINE float16 tanpif16(float16 x) {
   if (LIBC_UNLIKELY(x_abs == 0U))
     return x;
 
+  // tanpi(x) underflows for 0 < |x| <= 0x1.45p-16.
+  if (LIBC_UNLIKELY(x_abs <= 0x0145U)) {
+    fputil::set_errno_if_required(ERANGE);
+    fputil::raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
+  }
+
   if (LIBC_UNLIKELY(fputil::is_integer(x))) {
     // Preserve the input sign for even integers and flip it for odd integers.
     Sign sign =
@@ -60,6 +66,11 @@ LIBC_INLINE float16 tanpif16(float16 x) {
     return FPBits::zero(sign).get_val();
   }
 
+  // Odd multiples of 1/4 have exact results of +1 or -1.
+  float xf = x;
+  if (LIBC_UNLIKELY(fputil::is_odd_integer(4.0f * xf)))
+    return fputil::is_odd_integer(2.0f * xf - 0.5f) ? -1.0f16 : 1.0f16;
+
 #ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
   // Handle exceptional values.
   if (LIBC_UNLIKELY(x_abs <= 0x4335)) {
@@ -103,12 +114,11 @@ LIBC_INLINE float16 tanpif16(float16 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)
-  float xf = x;
   float sin_k = 0, cos_k = 0, sin_y = 0, cosm1_y = 0;
   sincospif16_eval(xf, sin_k, cos_k, sin_y, cosm1_y);
 
   if (LIBC_UNLIKELY(sin_y == 0 && cos_k == 0)) {
-    fputil::set_errno_if_required(EDOM);
+    fputil::set_errno_if_required(ERANGE);
     fputil::raise_except_if_required(FE_DIVBYZERO);
 
     int16_t x_mp5_u = static_cast<int16_t>(x - 0.5);
diff --git a/libc/test/src/math/smoke/tanpif16_test.cpp b/libc/test/src/math/smoke/tanpif16_test.cpp
index b005dfb92ed28e..bd2cea00984419 100644
--- a/libc/test/src/math/smoke/tanpif16_test.cpp
+++ b/libc/test/src/math/smoke/tanpif16_test.cpp
@@ -31,4 +31,16 @@ TEST_F(LlvmLibcTanpif16Test, SpecialNumbers) {
 
   EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::tanpif16(neg_inf));
   EXPECT_MATH_ERRNO(EDOM);
+
+  EXPECT_FP_EQ_WITH_EXCEPTION(0x1.fe8p-15f16,
+                              LIBC_NAMESPACE::tanpif16(0x1.45p-16f16),
+                              FE_UNDERFLOW | FE_INEXACT);
+  EXPECT_MATH_ERRNO(ERANGE);
+
+  EXPECT_FP_EQ_WITH_EXCEPTION(1.0f16, LIBC_NAMESPACE::tanpif16(2.25f16), 0);
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::tanpif16(0.5f16),
+                              FE_DIVBYZERO);
+  EXPECT_MATH_ERRNO(ERANGE);
 }
diff --git a/libc/test/src/math/smoke/tanpif_test.cpp b/libc/test/src/math/smoke/tanpif_test.cpp
index 0504c9a7bf8130..98e82801716080 100644
--- a/libc/test/src/math/smoke/tanpif_test.cpp
+++ b/libc/test/src/math/smoke/tanpif_test.cpp
@@ -31,4 +31,15 @@ TEST_F(LlvmLibcTanpifTest, SpecialNumbers) {
 
   EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::tanpif(neg_inf));
   EXPECT_MATH_ERRNO(EDOM);
+
+  EXPECT_FP_EQ_WITH_EXCEPTION(0x1.1abe4p-130f,
+                              LIBC_NAMESPACE::tanpif(0x1.68p-132f),
+                              FE_UNDERFLOW | FE_INEXACT);
+  EXPECT_MATH_ERRNO(ERANGE);
+
+  EXPECT_FP_EQ_WITH_EXCEPTION(1.0f, LIBC_NAMESPACE::tanpif(2.25f), 0);
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ_WITH_EXCEPTION(inf, LIBC_NAMESPACE::tanpif(0.5f), FE_DIVBYZERO);
+  EXPECT_MATH_ERRNO(ERANGE);
 }



More information about the libc-commits mailing list