[libc-commits] [libc] [libc][math] Use float-only evaluation in asinbf16 and acosbf16 (PR #228734)

via libc-commits libc-commits at lists.llvm.org
Sat Oct 3 09:14:59 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libc

Author: Chen Miao (ChenMiaoi)

<details>
<summary>Changes</summary>

Update `asinbf16` and `acosbf16` to use a shared `float` polynomial instead of the double-precision `asin_eval` helper. Reuse the polynomial coefficients from `asinf16`.

For small `acosbf16` inputs, return pi/2 rounded to `bfloat16` before squaring the input or evaluating the polynomial. This avoids spurious underflow from intermediate products.

Related: #<!-- -->202079
Assisted-by: GPT-6-Astra

---
Full diff: https://github.com/llvm/llvm-project/pull/228734.diff


4 Files Affected:

- (modified) libc/src/__support/math/acosbf16.h (+10-11) 
- (modified) libc/src/__support/math/asin_utils.h (+10) 
- (modified) libc/src/__support/math/asinbf16.h (+2-4) 
- (modified) libc/test/src/math/smoke/acosbf16_test.cpp (+25) 


``````````diff
diff --git a/libc/src/__support/math/acosbf16.h b/libc/src/__support/math/acosbf16.h
index 7b78bd34dd2876..e0fb861accb475 100644
--- a/libc/src/__support/math/acosbf16.h
+++ b/libc/src/__support/math/acosbf16.h
@@ -35,6 +35,14 @@ LIBC_INLINE bfloat16 acosbf16(bfloat16 x) {
   uint16_t x_u = xbits.uintval();
   uint16_t x_abs = x_u & 0x7fff;
   bool sign = (x_u >> 15);
+
+  // For |x| <= 2^-12, 0x1.92p0 < acos(x) < 0x1.93p0: the lower bound is
+  // a bfloat16 value and the upper bound is the midpoint to its successor.
+  // Thus acos(x) rounds like pi/2 in every rounding mode.  Return before
+  // squaring x or evaluating the polynomial to avoid intermediate underflow.
+  if (LIBC_UNLIKELY(x_abs <= 0x3980))
+    return fputil::cast<bfloat16>(PI_2);
+
   float xf = x;
 
   float xf_abs = (xf < 0 ? -xf : xf);
@@ -42,12 +50,7 @@ LIBC_INLINE bfloat16 acosbf16(bfloat16 x) {
 
   // case 1: x <= 0.5
   if (x_abs <= 0x3F00) {
-    // |x| = {0}
-    if (LIBC_UNLIKELY(x_abs == 0))
-      return fputil::cast<bfloat16>(PI_2);
-
-    float xp = fputil::cast<float>(asin_internal::asin_eval(x_sq));
-    float result = xf * fputil::multiply_add(x_sq, xp, 1.0f);
+    float result = xf * asin_internal::asin_eval_float(x_sq);
     return fputil::cast<bfloat16>(PI_2 - result);
   }
 
@@ -66,11 +69,7 @@ LIBC_INLINE bfloat16 acosbf16(bfloat16 x) {
     // and acos(x) = acos(|x|) for x >= 0, pi - acos(|x|) for x < 0
     float t = fputil::multiply_add<float>(xf_abs, -0.5f, 0.5f);
     float t_sqrt = fputil::sqrt<float>(t);
-    // TODO: Use bfloat16 version for inv_trigf_utils_internals after they are
-    // available Tracking issue :
-    // https://github.com/llvm/llvm-project/issues/202079
-    float tp = fputil::cast<float>(asin_internal::asin_eval(t));
-    float asin_sqrt_t = t_sqrt * (fputil::multiply_add(t, tp, 1.0f));
+    float asin_sqrt_t = t_sqrt * asin_internal::asin_eval_float(t);
 
     return fputil::cast<bfloat16>(
         (sign) ? fputil::multiply_add(asin_sqrt_t, -2.0f, PI)
diff --git a/libc/src/__support/math/asin_utils.h b/libc/src/__support/math/asin_utils.h
index f6f19baa312fb3..f1b15631e262f5 100644
--- a/libc/src/__support/math/asin_utils.h
+++ b/libc/src/__support/math/asin_utils.h
@@ -21,6 +21,16 @@ namespace LIBC_NAMESPACE_DECL {
 
 namespace asin_internal {
 
+// Single-precision approximation of asin(x) / x for |x| <= 0.5, evaluated
+// at xsq = x * x.  The coefficients are the same as those used by asinf16,
+// generated with Sollya:
+// > P = fpminimax(asin(x)/x, [|0, 2, 4, 6, 8|], [|SG...|], [0, 0.5]);
+// This approximation is intended for 16-bit results, not float results.
+LIBC_INLINE LIBC_CONSTEXPR float asin_eval_float(float xsq) {
+  return fputil::polyeval(xsq, 0x1.000002p0f, 0x1.554c2ap-3f, 0x1.3541ccp-4f,
+                          0x1.43b2d6p-5f, 0x1.a0d73ep-5f);
+}
+
 using DoubleDouble = fputil::DoubleDouble;
 using DFloat128 = fputil::DyadicFloat<128>;
 
diff --git a/libc/src/__support/math/asinbf16.h b/libc/src/__support/math/asinbf16.h
index 64c113e68856f5..9083ca9adcce91 100644
--- a/libc/src/__support/math/asinbf16.h
+++ b/libc/src/__support/math/asinbf16.h
@@ -55,8 +55,7 @@ LIBC_INLINE LIBC_CONSTEXPR bfloat16 asinbf16(bfloat16 x) {
       return x;
     }
 
-    float xp = fputil::cast<float>(asin_internal::asin_eval(x_sq));
-    float result = xf * (fputil::multiply_add<float>(x_sq, xp, 1.0f));
+    float result = xf * asin_internal::asin_eval_float(x_sq);
     return fputil::cast<bfloat16>(result);
   }
 
@@ -70,8 +69,7 @@ LIBC_INLINE LIBC_CONSTEXPR bfloat16 asinbf16(bfloat16 x) {
 
     float t = fputil::multiply_add<float>(xf_abs, -0.5f, 0.5f);
     float t_sqrt = fputil::sqrt<float>(t);
-    float tp = fputil::cast<float>(asin_internal::asin_eval(t));
-    float asin_sqrt_t = t_sqrt * (fputil::multiply_add<float>(t, tp, 1.0f));
+    float asin_sqrt_t = t_sqrt * asin_internal::asin_eval_float(t);
     float result = fputil::multiply_add<float>(-2.0f, asin_sqrt_t, PI_2);
     return fputil::cast<bfloat16>(x_sign * result);
   }
diff --git a/libc/test/src/math/smoke/acosbf16_test.cpp b/libc/test/src/math/smoke/acosbf16_test.cpp
index 4ca738446b9904..a477a61c236185 100644
--- a/libc/test/src/math/smoke/acosbf16_test.cpp
+++ b/libc/test/src/math/smoke/acosbf16_test.cpp
@@ -49,3 +49,28 @@ class LlvmLibcAcosBf16Test : public LIBC_NAMESPACE::testing::FEnvSafeTest {
   }
 };
 TEST_F(LlvmLibcAcosBf16Test, SpecialNumbers) { test_special_numbers(); }
+
+TEST_F(LlvmLibcAcosBf16Test, SmallInputs) {
+  using FPBits = LIBC_NAMESPACE::fputil::FPBits<bfloat16>;
+  auto acos_without_underflow = [](bfloat16 x) {
+    LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
+    bfloat16 result = LIBC_NAMESPACE::acosbf16(x);
+    EXPECT_EQ(LIBC_NAMESPACE::fputil::test_except(FE_UNDERFLOW), 0);
+    EXPECT_MATH_ERRNO(0);
+    return result;
+  };
+
+  // Include subnormals, an input that caused an intermediate underflow in
+  // the float polynomial without FMA, and the small-input branch boundary.
+  const uint16_t inputs[] = {0x0001, 0x007f, 0x0080, 0x209d,
+                             0x397f, 0x3980, 0x3981};
+  const bfloat16 pi_2_lo = FPBits(uint16_t(0x3fc9)).get_val();
+  const bfloat16 pi_2_hi = FPBits(uint16_t(0x3fca)).get_val();
+  for (uint16_t bits : inputs) {
+    bfloat16 x = FPBits(bits).get_val();
+    EXPECT_FP_EQ_ALL_ROUNDING(pi_2_lo, pi_2_hi, pi_2_lo, pi_2_lo,
+                              acos_without_underflow(x));
+    EXPECT_FP_EQ_ALL_ROUNDING(pi_2_lo, pi_2_hi, pi_2_lo, pi_2_lo,
+                              acos_without_underflow(-x));
+  }
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/228734


More information about the libc-commits mailing list