[libc-commits] [libc] [libc][math] Fix `tanpif` and `tanpif16` error handling (PR #235296)
via libc-commits
libc-commits at lists.llvm.org
Sat Oct 24 06:41:32 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libc
Author: Connector Switch (c8ef)
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/235296.diff
4 Files Affected:
- (modified) libc/src/__support/math/tanpif.h (+10-2)
- (modified) libc/src/__support/math/tanpif16.h (+12-2)
- (modified) libc/test/src/math/smoke/tanpif16_test.cpp (+12)
- (modified) libc/test/src/math/smoke/tanpif_test.cpp (+11)
``````````diff
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);
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/235296
More information about the libc-commits
mailing list