[libc-commits] [libc] [libc][math][c23] Add atanf16() function (PR #141612)

via libc-commits libc-commits at lists.llvm.org
Tue May 27 07:36:24 PDT 2025


https://github.com/wldfngrs created https://github.com/llvm/llvm-project/pull/141612

- Implementation of atan (tan inverse) function for 16-bit inputs.
- Exhaustive tests across the 16-bit input range

>From 7f4c00577e782a250ab0c84724db5ffe1468c0be Mon Sep 17 00:00:00 2001
From: wldfngrs <wldfngrs at gmail.com>
Date: Tue, 27 May 2025 15:27:40 +0100
Subject: [PATCH] add atanf16() function

---
 libc/config/linux/x86_64/entrypoints.txt  |   1 +
 libc/docs/headers/math/index.rst          |   4 +-
 libc/include/math.yaml                    |   7 ++
 libc/src/math/CMakeLists.txt              |   1 +
 libc/src/math/atanf16.h                   |  20 +++++
 libc/src/math/generic/CMakeLists.txt      |  20 +++++
 libc/src/math/generic/atanf16.cpp         | 104 ++++++++++++++++++++++
 libc/test/src/math/CMakeLists.txt         |  11 +++
 libc/test/src/math/atanf16_test.cpp       |  32 +++++++
 libc/test/src/math/smoke/CMakeLists.txt   |  11 +++
 libc/test/src/math/smoke/atanf16_test.cpp |  36 ++++++++
 11 files changed, 245 insertions(+), 2 deletions(-)
 create mode 100644 libc/src/math/atanf16.h
 create mode 100644 libc/src/math/generic/atanf16.cpp
 create mode 100644 libc/test/src/math/atanf16_test.cpp
 create mode 100644 libc/test/src/math/smoke/atanf16_test.cpp

diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 9f447dd0d35d2..ef765f4004dba 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -667,6 +667,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.acospif16
     libc.src.math.asinf16
     libc.src.math.asinhf16
+    libc.src.math.atanf16
     libc.src.math.atanhf16
     libc.src.math.canonicalizef16
     libc.src.math.ceilf16
diff --git a/libc/docs/headers/math/index.rst b/libc/docs/headers/math/index.rst
index 6b0365f481a4c..3cb41a6871b94 100644
--- a/libc/docs/headers/math/index.rst
+++ b/libc/docs/headers/math/index.rst
@@ -255,13 +255,13 @@ Higher Math Functions
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | acospi    |                  |                 |                        | |check|              |                        | 7.12.4.8               | F.10.1.8                   |
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
-| asin      | |check|          | |check|         |                        |                      |                        | 7.12.4.2               | F.10.1.2                   |
+| asin      | |check|          | |check|         |                        | |check|              |                        | 7.12.4.2               | F.10.1.2                   |
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | asinh     | |check|          |                 |                        | |check|              |                        | 7.12.5.2               | F.10.2.2                   |
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | asinpi    |                  |                 |                        |                      |                        | 7.12.4.9               | F.10.1.9                   |
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
-| atan      | |check|          | 1 ULP           |                        |                      |                        | 7.12.4.3               | F.10.1.3                   |
+| atan      | |check|          | 1 ULP           |                        | |check|              |                        | 7.12.4.3               | F.10.1.3                   |
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
 | atan2     | |check|          | 1 ULP           |                        |                      | 1 ULP                  | 7.12.4.4               | F.10.1.4                   |
 +-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
diff --git a/libc/include/math.yaml b/libc/include/math.yaml
index 0a69653d156af..466c08ade6fc4 100644
--- a/libc/include/math.yaml
+++ b/libc/include/math.yaml
@@ -120,6 +120,13 @@ functions:
     return_type: float
     arguments:
       - type: float
+  - name: atanf16
+    standards:
+      - stdc
+    return_type: _Float16
+    arguments:
+      - type: _Float16
+    guard: LIBC_TYPES_HAS_FLOAT16
   - name: atanhf
     standards:
       - stdc
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index 055e76468a461..b27b0d2b523f8 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -60,6 +60,7 @@ add_math_entrypoint_object(asinhf16)
 
 add_math_entrypoint_object(atan)
 add_math_entrypoint_object(atanf)
+add_math_entrypoint_object(atanf16)
 
 add_math_entrypoint_object(atan2)
 add_math_entrypoint_object(atan2f)
diff --git a/libc/src/math/atanf16.h b/libc/src/math/atanf16.h
new file mode 100644
index 0000000000000..113fdc4600a9e
--- /dev/null
+++ b/libc/src/math/atanf16.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for atanf16 -----------------------*- 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_MATH_ATANF16_H
+#define LLVM_LIBC_SRC_MATH_ATANF16_H
+
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+float16 atanf16(float16 x);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_ATANF16_H
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index e5d1f9075bcb1..27fe481cc64cb 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -4234,6 +4234,26 @@ add_entrypoint_object(
     libc.src.__support.macros.optimization
 )
 
+add_entrypoint_object(
+  atanf16
+  SRCS
+    atanf16.cpp
+  HDRS
+    ../atanf16.h
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.fenv_macros
+    libc.src.__support.FPUtil.cast
+    libc.src.__support.FPUtil.except_value_utils
+    libc.src.__support.FPUtil.fenv_impl
+    libc.src.__support.FPUtil.fp_bits
+    libc.src.__support.FPUtil.multiply_add
+    libc.src.__support.FPUtil.polyeval
+    libc.src.__support.FPUtil.sqrt
+    libc.src.__support.macros.optimization
+    libc.src.__support.macros.properties.types
+)
+
 add_entrypoint_object(
   atan
   SRCS
diff --git a/libc/src/math/generic/atanf16.cpp b/libc/src/math/generic/atanf16.cpp
new file mode 100644
index 0000000000000..c1a349d7719ed
--- /dev/null
+++ b/libc/src/math/generic/atanf16.cpp
@@ -0,0 +1,104 @@
+//===-- Half-precision atanf16(x) function --------------------------------===//
+//
+// 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.
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/math/atanf16.h"
+#include "hdr/errno_macros.h"
+#include "hdr/fenv_macros.h"
+#include "src/__support/FPUtil/FEnvImpl.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/FPUtil/PolyEval.h"
+#include "src/__support/FPUtil/cast.h"
+#include "src/__support/FPUtil/except_value_utils.h"
+#include "src/__support/FPUtil/multiply_add.h"
+#include "src/__support/FPUtil/sqrt.h"
+#include "src/__support/macros/optimization.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// Generated by Solly using the following command:
+// > round(pi/2, D, RN);
+static constexpr float PI_2 = 0x1.921fb54442d18p0f;
+
+#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+static constexpr size_t N_EXCEPTS = 6;
+
+static constexpr fputil::ExceptValues<float16, N_EXCEPTS> ATANF16_EXCEPTS{{
+    // (input, RZ output, RU offset, RD offset, RN offset)
+    {0x2745, 0x2744, 1, 0, 1},
+    {0x3099, 0x3090, 1, 0, 1},
+    {0x3c6c, 0x3aae, 1, 0, 1},
+    {0x466e, 0x3daa, 1, 0, 1},
+    {0x48ae, 0x3ddb, 1, 0, 0},
+    {0x5619, 0x3e3d, 1, 0, 1},
+}};
+#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+
+LLVM_LIBC_FUNCTION(float16, atanf16, (float16 x)) {
+  using FPBits = fputil::FPBits<float16>;
+  FPBits xbits(x);
+
+  uint16_t x_u = xbits.uintval();
+  uint16_t x_abs = x_u & 0x7fff;
+  bool x_sign = x_u >> 15;
+  float sign = (x_sign ? -1.0 : 1.0);
+
+  // |x| >= +/-inf
+  if (LIBC_UNLIKELY(x_abs >= 0x7c00U)) {
+    if (xbits.is_nan()) {
+      if (xbits.is_signaling_nan()) {
+        fputil::raise_except_if_required(FE_INVALID);
+        return FPBits::quiet_nan().get_val();
+      }
+      return x;
+    }
+
+    // atanf16(+/-inf) = +/-pi/2
+    return fputil::cast<float16>(sign * PI_2);
+  }
+
+  float xf = x;
+  float xsq = xf * xf;
+#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+  // Handle exceptional values
+  if (auto r = ATANF16_EXCEPTS.lookup_odd(x_abs, x_sign);
+      LIBC_UNLIKELY(r.has_value()))
+    return r.value();
+#endif
+
+  // |x| <= 0x1p0, |x| <= 1
+  if (x_abs <= 0x3C00) {
+    // atanf16(+/-0) = +/-0
+    if (LIBC_UNLIKELY(x_abs == 0)) {
+      return x;
+    }
+
+    // Degree-14 minimax odd polynomial of atan(x) generated by Sollya with:
+    // > P = fpminimax(atan(x)/x, [|0, 2, 4, 6, 8, 10, 12, 14|], [|SG...|], [0,
+    // 1]);
+    float result = fputil::polyeval(
+        xsq, 0x1.fffffcp-1f, -0x1.55519ep-2f, 0x1.98f6a8p-3f, -0x1.1f0a92p-3f,
+        0x1.95b654p-4f, -0x1.e65492p-5f, 0x1.8c0c36p-6f, -0x1.32316ep-8f);
+    return fputil::cast<float16>(xf * result);
+  }
+
+  // If |x| > 1
+  // y = atan(x)
+  // y = atan(|x|) = pi/2 - atan(1/|x|)
+  // Recall, 1/|x| < 1
+  float x_inv_sq = 1 / xsq;
+  float x_inv = fputil::sqrt<float>(x_inv_sq);
+  float interm =
+      fputil::polyeval(x_inv_sq, 0x1.fffffcp-1f, -0x1.55519ep-2f,
+                       0x1.98f6a8p-3f, -0x1.1f0a92p-3f, 0x1.95b654p-4f,
+                       -0x1.e65492p-5f, 0x1.8c0c36p-6f, -0x1.32316ep-8f);
+
+  return fputil::cast<float16>(sign *
+                               fputil::multiply_add(x_inv, -interm, PI_2));
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 9074524403102..0d0232335eb5f 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -2338,6 +2338,17 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  atanf16_test
+  NEED_MPFR
+  SUITE
+    libc-math-unittests
+  SRCS
+    atanf16_test.cpp
+  DEPENDS
+    libc.src.math.atanf16
+)
+
 add_fp_unittest(
   scalbn_test
   NEED_MPFR
diff --git a/libc/test/src/math/atanf16_test.cpp b/libc/test/src/math/atanf16_test.cpp
new file mode 100644
index 0000000000000..2352dca0637b3
--- /dev/null
+++ b/libc/test/src/math/atanf16_test.cpp
@@ -0,0 +1,32 @@
+#include "src/math/atanf16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+using LlvmLibcAtanf16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
+
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+// Range: [0, Inf]
+static constexpr uint16_t POS_START = 0x0000U;
+static constexpr uint16_t POS_STOP = 0x7c00U;
+
+// Range: [-Inf, 0]
+static constexpr uint16_t NEG_START = 0x8000U;
+static constexpr uint16_t NEG_STOP = 0xfc00U;
+
+TEST_F(LlvmLibcAtanf16Test, PositiveRange) {
+  for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
+    float16 x = FPBits(v).get_val();
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan, x,
+                                   LIBC_NAMESPACE::atanf16(x), 0.5);
+  }
+}
+
+TEST_F(LlvmLibcAtanf16Test, NegativeRange) {
+  for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
+    float16 x = FPBits(v).get_val();
+    EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan, x,
+                                   LIBC_NAMESPACE::atanf16(x), 0.5);
+  }
+}
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 55edffb4f5bdf..599939cbec4b5 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -4107,6 +4107,17 @@ add_fp_unittest(
     libc.src.math.atan
 )
 
+add_fp_unittest(
+  atanf16_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    atanf16_test.cpp
+  DEPENDS
+    libc.src.errno.errno
+    libc.src.math.atanf16
+)
+
 add_fp_unittest(
   atan2f_test
   SUITE
diff --git a/libc/test/src/math/smoke/atanf16_test.cpp b/libc/test/src/math/smoke/atanf16_test.cpp
new file mode 100644
index 0000000000000..b6699d65297c6
--- /dev/null
+++ b/libc/test/src/math/smoke/atanf16_test.cpp
@@ -0,0 +1,36 @@
+//===-- Unittests for atanf16 ---------------------------------------------===//
+//
+//
+// 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.
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/errno/libc_errno.h"
+#include "src/math/atanf16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcAtanf16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
+
+TEST_F(LlvmLibcAtanf16Test, SpecialNumbers) {
+  LIBC_NAMESPACE::libc_errno = 0;
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::atanf16(aNaN));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::atanf16(sNaN), FE_INVALID);
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(zero, LIBC_NAMESPACE::atanf16(zero));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(neg_zero, LIBC_NAMESPACE::atanf16(neg_zero));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(0x1.92p0, LIBC_NAMESPACE::atanf16(inf));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(-0x1.92p0, LIBC_NAMESPACE::atanf16(neg_inf));
+  EXPECT_MATH_ERRNO(0);
+}



More information about the libc-commits mailing list