[libc-commits] [libc] [llvm] [libc][math][c23] Add atan2f16 function (PR #183531)

Xinlong Chen via libc-commits libc-commits at lists.llvm.org
Sat Feb 28 00:38:22 PST 2026


https://github.com/Xinlong-Chen updated https://github.com/llvm/llvm-project/pull/183531

>From 6acc97b587d51f39b7e12883da79946045dc3d20 Mon Sep 17 00:00:00 2001
From: xinlongchen <xinlongchen at tencent.com>
Date: Thu, 26 Feb 2026 21:07:29 +0800
Subject: [PATCH 1/4] [libc][math][c23] Add atan2f16 function

---
 libc/config/baremetal/aarch64/entrypoints.txt |   1 +
 libc/config/baremetal/arm/entrypoints.txt     |   1 +
 libc/config/baremetal/riscv/entrypoints.txt   |   1 +
 libc/config/darwin/aarch64/entrypoints.txt    |   1 +
 libc/config/gpu/amdgpu/entrypoints.txt        |   1 +
 libc/config/gpu/nvptx/entrypoints.txt         |   1 +
 libc/config/linux/aarch64/entrypoints.txt     |   1 +
 libc/config/linux/riscv/entrypoints.txt       |   1 +
 libc/config/linux/x86_64/entrypoints.txt      |   1 +
 libc/shared/math.h                            |   1 +
 libc/shared/math/atan2f16.h                   |  28 ++++
 libc/src/__support/math/CMakeLists.txt        |  13 ++
 libc/src/__support/math/atan2f16.h            | 150 ++++++++++++++++++
 libc/src/math/CMakeLists.txt                  |   1 +
 libc/src/math/atan2f16.h                      |  21 +++
 libc/src/math/generic/CMakeLists.txt          |  10 ++
 libc/src/math/generic/atan2f16.cpp            |  18 +++
 libc/test/src/math/CMakeLists.txt             |  12 ++
 libc/test/src/math/atan2f16_test.cpp          | 142 +++++++++++++++++
 libc/test/src/math/smoke/CMakeLists.txt       |  11 ++
 libc/test/src/math/smoke/atan2f16_test.cpp    |  62 ++++++++
 .../llvm-project-overlay/libc/BUILD.bazel     |  21 +++
 .../libc/test/src/math/smoke/BUILD.bazel      |   2 +
 23 files changed, 501 insertions(+)
 create mode 100644 libc/shared/math/atan2f16.h
 create mode 100644 libc/src/__support/math/atan2f16.h
 create mode 100644 libc/src/math/atan2f16.h
 create mode 100644 libc/src/math/generic/atan2f16.cpp
 create mode 100644 libc/test/src/math/atan2f16_test.cpp
 create mode 100644 libc/test/src/math/smoke/atan2f16_test.cpp

diff --git a/libc/config/baremetal/aarch64/entrypoints.txt b/libc/config/baremetal/aarch64/entrypoints.txt
index 582e7c52726fe..4367e509cf168 100644
--- a/libc/config/baremetal/aarch64/entrypoints.txt
+++ b/libc/config/baremetal/aarch64/entrypoints.txt
@@ -586,6 +586,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.acoshf16
     libc.src.math.asinf16
     libc.src.math.asinhf16
+    libc.src.math.atan2f16
     libc.src.math.canonicalizef16
     libc.src.math.ceilf16
     libc.src.math.copysignf16
diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt
index 98374e6cede73..9e4779aa2df8d 100644
--- a/libc/config/baremetal/arm/entrypoints.txt
+++ b/libc/config/baremetal/arm/entrypoints.txt
@@ -589,6 +589,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.acoshf16
     libc.src.math.asinf16
     libc.src.math.asinhf16
+    libc.src.math.atan2f16
     libc.src.math.canonicalizef16
     libc.src.math.ceilf16
     libc.src.math.copysignf16
diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt
index b45b388ec9634..1474805dd82bc 100644
--- a/libc/config/baremetal/riscv/entrypoints.txt
+++ b/libc/config/baremetal/riscv/entrypoints.txt
@@ -587,6 +587,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.acoshf16
     libc.src.math.asinf16
     libc.src.math.asinhf16
+    libc.src.math.atan2f16
     libc.src.math.canonicalizef16
     libc.src.math.ceilf16
     libc.src.math.copysignf16
diff --git a/libc/config/darwin/aarch64/entrypoints.txt b/libc/config/darwin/aarch64/entrypoints.txt
index 5488bf1d0c7de..bdec499f5fb59 100644
--- a/libc/config/darwin/aarch64/entrypoints.txt
+++ b/libc/config/darwin/aarch64/entrypoints.txt
@@ -403,6 +403,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.acoshf16
     libc.src.math.asinf16
     libc.src.math.asinhf16
+    libc.src.math.atan2f16
     libc.src.math.canonicalizef16
     libc.src.math.ceilf16
     libc.src.math.copysignf16
diff --git a/libc/config/gpu/amdgpu/entrypoints.txt b/libc/config/gpu/amdgpu/entrypoints.txt
index 856bf1d55f3be..ffd1b000189b5 100644
--- a/libc/config/gpu/amdgpu/entrypoints.txt
+++ b/libc/config/gpu/amdgpu/entrypoints.txt
@@ -520,6 +520,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.asinf16
     libc.src.math.asinhf16
     libc.src.math.atanf16
+    libc.src.math.atan2f16
     libc.src.math.atanhf16
     libc.src.math.canonicalizef16
     libc.src.math.ceilf16
diff --git a/libc/config/gpu/nvptx/entrypoints.txt b/libc/config/gpu/nvptx/entrypoints.txt
index 8765cf189f325..d95fe49e9c172 100644
--- a/libc/config/gpu/nvptx/entrypoints.txt
+++ b/libc/config/gpu/nvptx/entrypoints.txt
@@ -522,6 +522,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.asinf16
     libc.src.math.asinhf16
     libc.src.math.atanf16
+    libc.src.math.atan2f16
     libc.src.math.atanhf16
     libc.src.math.canonicalizef16
     libc.src.math.ceilf16
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index d27dabaff5c19..da6b5357fa110 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -666,6 +666,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     # math.h C23 _Float16 entrypoints
     libc.src.math.acoshf16
     libc.src.math.asinpif16
+    libc.src.math.atan2f16
     libc.src.math.canonicalizef16
     libc.src.math.ceilf16
     libc.src.math.copysignf16
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index b823e1b25e6cc..874cc01a5b6ee 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -677,6 +677,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.acospif16
     libc.src.math.asinf16
     libc.src.math.asinhf16
+    libc.src.math.atan2f16
     libc.src.math.atanhf16
     libc.src.math.canonicalizef16
     libc.src.math.ceilf16
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index f1b3e90cfd031..702b66023440f 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -722,6 +722,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
     libc.src.math.asinhf16
     libc.src.math.asinpif16
     libc.src.math.atanf16
+    libc.src.math.atan2f16
     libc.src.math.atanhf16
     libc.src.math.atanpif16
     libc.src.math.canonicalizef16
diff --git a/libc/shared/math.h b/libc/shared/math.h
index 4ab336989f794..81546c0d26d9d 100644
--- a/libc/shared/math.h
+++ b/libc/shared/math.h
@@ -28,6 +28,7 @@
 #include "math/atan2.h"
 #include "math/atan2f.h"
 #include "math/atan2f128.h"
+#include "math/atan2f16.h"
 #include "math/atanf.h"
 #include "math/atanf16.h"
 #include "math/atanhf.h"
diff --git a/libc/shared/math/atan2f16.h b/libc/shared/math/atan2f16.h
new file mode 100644
index 0000000000000..b9d42f579757c
--- /dev/null
+++ b/libc/shared/math/atan2f16.h
@@ -0,0 +1,28 @@
+//===-- Shared atan2f16 function --------------------------------*- 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_SHARED_MATH_ATAN2F16_H
+#define LLVM_LIBC_SHARED_MATH_ATAN2F16_H
+
+#include "shared/libc_common.h"
+
+#ifdef LIBC_TYPES_HAS_FLOAT16
+
+#include "src/__support/math/atan2f16.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace shared {
+
+using math::atan2f16;
+
+} // namespace shared
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LIBC_TYPES_HAS_FLOAT16
+
+#endif // LLVM_LIBC_SHARED_MATH_ATAN2F16_H
diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt
index 4e7abd280bc4b..e5d12e71737c6 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -245,6 +245,19 @@ add_header_library(
     libc.src.__support.macros.optimization
 )
 
+add_header_library(
+  atan2f16
+  HDRS
+    atan2f16.h
+  DEPENDS
+    libc.src.__support.FPUtil.cast
+    libc.src.__support.FPUtil.fenv_impl
+    libc.src.__support.FPUtil.fp_bits
+    libc.src.__support.FPUtil.polyeval
+    libc.src.__support.macros.optimization
+    libc.include.llvm-libc-macros.float16_macros
+)
+
 add_header_library(
   atan2f128
   HDRS
diff --git a/libc/src/__support/math/atan2f16.h b/libc/src/__support/math/atan2f16.h
new file mode 100644
index 0000000000000..e9bf923c8ede2
--- /dev/null
+++ b/libc/src/__support/math/atan2f16.h
@@ -0,0 +1,150 @@
+//===-- Implementation header for atan2f16 ----------------------*- 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_ATAN2F16_H
+#define LLVM_LIBC_SRC___SUPPORT_MATH_ATAN2F16_H
+
+#include "include/llvm-libc-macros/float16-macros.h"
+
+#ifdef LIBC_TYPES_HAS_FLOAT16
+
+#include "src/__support/CPP/optional.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/macros/optimization.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+namespace math {
+
+namespace atan2f16_internal {
+
+#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+// (x_abs, y_abs) keys for exceptional atan2f16 inputs;
+// index i -> ExceptValues input.
+constexpr size_t N_ATAN2F16_EXCEPTS = 11;
+constexpr uint16_t ATAN2F16_EXCEPT_X[] = {0x37DA, 0x3A01, 0x3EFD, 0x4059,
+                                          0x40CD, 0x3D84, 0x38C2, 0x3814,
+                                          0x3596, 0x41B5, 0x3C62};
+constexpr uint16_t ATAN2F16_EXCEPT_Y[] = {0x3631, 0x3BFE, 0x398B, 0x3E2F,
+                                          0x4378, 0x354A, 0x3A93, 0x3C1F,
+                                          0x4189, 0x3CA3, 0x3EB3};
+
+// (input = index, RZ result, RU offset, RD offset, RN offset).
+constexpr fputil::ExceptValues<float16, N_ATAN2F16_EXCEPTS> ATAN2F16_EXCEPTS{{
+    {0, 0x3957, 1, 0, 0},
+    {1, 0x3B69, 1, 0, 0},
+    {2, 0x360A, 1, 0, 1},
+    {3, 0x38F2, 1, 0, 0},
+    {4, 0x3BFE, 1, 0, 1},
+    {5, 0x3387, 1, 0, 0},
+    {6, 0x3B8D, 1, 0, 1},
+    {7, 0x3C71, 1, 0, 1},
+    {8, 0x3DC7, 1, 0, 1},
+    {9, 0x362C, 1, 0, 1},
+    {10, 0x3BEE, 1, 0, 0},
+}};
+
+LIBC_INLINE constexpr cpp::optional<float16>
+lookup_atan2f16_except(uint16_t x_abs, uint16_t y_abs) {
+  for (size_t i = 0; i < N_ATAN2F16_EXCEPTS; ++i) {
+    if (x_abs == ATAN2F16_EXCEPT_X[i] && y_abs == ATAN2F16_EXCEPT_Y[i])
+      return ATAN2F16_EXCEPTS.lookup(static_cast<uint16_t>(i));
+  }
+  return cpp::nullopt;
+}
+#endif
+
+// atan(u) for u in (0, 1]: atan(u) = u * P(u^2).
+LIBC_INLINE float atan_u(float u) {
+  float u2 = u * u;
+  float p = fputil::polyeval(u2, 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 u * p;
+}
+
+} // namespace atan2f16_internal
+
+LIBC_INLINE float16 atan2f16(float16 y, float16 x) {
+  using FPBits = fputil::FPBits<float16>;
+  constexpr float PI = 0x1.921fb6p1f;
+  constexpr float PI_OVER_2 = 0x1.921fb6p0f;
+  constexpr float PI_OVER_4 = 0x1.921fb6p-1f;
+  constexpr float THREE_PI_OVER_4 = 0x1.2d97c8p1f;
+  constexpr float IS_NEG[2] = {1.0f, -1.0f};
+
+  // const_term[x_sign][y_sign][recip]; recip = (|x| < |y|)
+  constexpr float CONST_TERM[2][2][2] = {
+      {{0.0f, -PI_OVER_2}, {0.0f, -PI_OVER_2}},
+      {{-PI, PI_OVER_2}, {-PI, PI_OVER_2}},
+  };
+
+  FPBits x_bits(x), y_bits(y);
+  bool x_sign = x_bits.sign().is_neg();
+  bool y_sign = y_bits.sign().is_neg();
+  x_bits.set_sign(Sign::POS);
+  y_bits.set_sign(Sign::POS);
+  uint16_t x_abs = x_bits.uintval();
+  uint16_t y_abs = y_bits.uintval();
+  uint16_t max_abs = x_abs > y_abs ? x_abs : y_abs;
+  uint16_t min_abs = x_abs <= y_abs ? x_abs : y_abs;
+
+  if (LIBC_UNLIKELY(max_abs >= 0x7c00U || min_abs == 0)) {
+    if (x_bits.is_nan() || y_bits.is_nan()) {
+      if (FPBits(x).is_signaling_nan() || FPBits(y).is_signaling_nan())
+        fputil::raise_except_if_required(FE_INVALID);
+      return FPBits::quiet_nan().get_val();
+    }
+    float xf = static_cast<float>(x);
+    float yf = static_cast<float>(y);
+    size_t x_except = (xf == 0.0f) ? 0 : (x_abs == 0x7c00U ? 2 : 1);
+    size_t y_except = (yf == 0.0f) ? 0 : (y_abs == 0x7c00U ? 2 : 1);
+    constexpr float EXCEPTS[3][3][2] = {
+        {{0.0f, PI}, {0.0f, PI}, {0.0f, PI}},
+        {{PI_OVER_2, PI_OVER_2}, {0.0f, 0.0f}, {0.0f, PI}},
+        {{PI_OVER_2, PI_OVER_2},
+         {PI_OVER_2, PI_OVER_2},
+         {PI_OVER_4, THREE_PI_OVER_4}},
+    };
+    size_t x_neg = x_sign ? 1 : 0;
+    float r = IS_NEG[y_sign] * EXCEPTS[y_except][x_except][x_neg];
+    return fputil::cast<float16>(r);
+  }
+
+#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+  if (!x_sign && !y_sign) {
+    if (auto r = atan2f16_internal::lookup_atan2f16_except(x_abs, y_abs);
+        LIBC_UNLIKELY(r.has_value()))
+      return r.value();
+  }
+#endif
+
+  bool recip = x_abs < y_abs;
+  float final_sign = IS_NEG[(x_sign != y_sign) != recip];
+  float const_term = CONST_TERM[x_sign][y_sign][recip];
+
+  float n = FPBits(min_abs).get_val();
+  float d = FPBits(max_abs).get_val();
+  float u = n / d;
+
+  float atan_u_val = atan2f16_internal::atan_u(u);
+  float r = final_sign * (const_term + atan_u_val);
+  return fputil::cast<float16>(r);
+}
+
+} // namespace math
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LIBC_TYPES_HAS_FLOAT16
+
+#endif // LLVM_LIBC_SRC___SUPPORT_MATH_ATAN2F16_H
diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt
index 7a9370fe487e2..8c8efea93e8f3 100644
--- a/libc/src/math/CMakeLists.txt
+++ b/libc/src/math/CMakeLists.txt
@@ -67,6 +67,7 @@ add_math_entrypoint_object(atanf16)
 
 add_math_entrypoint_object(atan2)
 add_math_entrypoint_object(atan2f)
+add_math_entrypoint_object(atan2f16)
 add_math_entrypoint_object(atan2l)
 add_math_entrypoint_object(atan2f128)
 
diff --git a/libc/src/math/atan2f16.h b/libc/src/math/atan2f16.h
new file mode 100644
index 0000000000000..a2fedf52c3726
--- /dev/null
+++ b/libc/src/math/atan2f16.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for atan2f16 ----------------------*- 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_ATAN2F16_H
+#define LLVM_LIBC_SRC_MATH_ATAN2F16_H
+
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/types.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+float16 atan2f16(float16 y, float16 x);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_MATH_ATAN2F16_H
diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt
index bb2c6f673f082..f000c91a6303d 100644
--- a/libc/src/math/generic/CMakeLists.txt
+++ b/libc/src/math/generic/CMakeLists.txt
@@ -4185,6 +4185,16 @@ add_entrypoint_object(
     libc.src.__support.math.atan2f
 )
 
+add_entrypoint_object(
+  atan2f16
+  SRCS
+    atan2f16.cpp
+  HDRS
+    ../atan2f16.h
+  DEPENDS
+    libc.src.__support.math.atan2f16
+)
+
 add_entrypoint_object(
   atan2
   SRCS
diff --git a/libc/src/math/generic/atan2f16.cpp b/libc/src/math/generic/atan2f16.cpp
new file mode 100644
index 0000000000000..142ad233b5a17
--- /dev/null
+++ b/libc/src/math/generic/atan2f16.cpp
@@ -0,0 +1,18 @@
+//===-- Half-precision atan2f16 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/atan2f16.h"
+#include "src/__support/math/atan2f16.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(float16, atan2f16, (float16 y, float16 x)) {
+  return math::atan2f16(y, x);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt
index 47c05c650c70a..dc51037d3dd38 100644
--- a/libc/test/src/math/CMakeLists.txt
+++ b/libc/test/src/math/CMakeLists.txt
@@ -2686,6 +2686,18 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  atan2f16_test
+  NEED_MPFR
+  SUITE
+    libc-math-unittests
+  SRCS
+    atan2f16_test.cpp
+  DEPENDS
+    libc.src.math.atan2f16
+    libc.src.__support.FPUtil.fp_bits
+)
+
 add_fp_unittest(
   atan2_test
   NEED_MPFR
diff --git a/libc/test/src/math/atan2f16_test.cpp b/libc/test/src/math/atan2f16_test.cpp
new file mode 100644
index 0000000000000..b4214e932f591
--- /dev/null
+++ b/libc/test/src/math/atan2f16_test.cpp
@@ -0,0 +1,142 @@
+//===-- Unittests for atan2f16 --------------------------------------------===//
+//
+// 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/__support/FPUtil/FPBits.h"
+#include "src/__support/macros/optimization.h"
+#include "src/math/atan2f16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+#define TOLERANCE 1
+#else
+#define TOLERANCE 0
+#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+
+using LlvmLibcAtan2f16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
+using LIBC_NAMESPACE::testing::tlog;
+
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+TEST_F(LlvmLibcAtan2f16Test, TrickyInputs) {
+  constexpr int N = 17;
+  mpfr::BinaryInput<float16> INPUTS[N] = {
+      {0x1.0p0f, 0x1.4p0f},   {0x1.2p0f, 0x1.0p0f},   {-0x1.0p0f, 0x1.2p0f},
+      {0x1.8p0f, 0x1.0p0f},   {0x1.2p-2f, 0x1.0p-2f}, {0x1.0p0f, 0x1.0p0f},
+      {0x1.0p-1f, 0x1.0p0f},  {0x1.0p0f, 0x1.0p-1f},  {0x1.8p1f, 0x1.0p0f},
+      {0x1.0p0f, 0x1.4p1f},   {0x1.2p1f, 0x1.0p-1f},  {0x1.0p-2f, 0x1.0p0f},
+      {0x1.0p0f, 0x1.0p-3f},  {0x1.4p0f, 0x1.0p0f},   {0x1.0p0f, 0x1.8p0f},
+      {0x1.0p-1f, 0x1.0p-1f}, {0x1.2p0f, 0x1.2p0f},
+  };
+
+  for (int i = 0; i < N; ++i) {
+    float16 x = INPUTS[i].x;
+    float16 y = INPUTS[i].y;
+    ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan2, INPUTS[i],
+                                   LIBC_NAMESPACE::atan2f16(x, y),
+                                   TOLERANCE + 0.5);
+    INPUTS[i].x = -INPUTS[i].x;
+    ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan2, INPUTS[i],
+                                   LIBC_NAMESPACE::atan2f16(-x, y),
+                                   TOLERANCE + 0.5);
+    INPUTS[i].y = -INPUTS[i].y;
+    ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan2, INPUTS[i],
+                                   LIBC_NAMESPACE::atan2f16(-x, -y),
+                                   TOLERANCE + 0.5);
+    INPUTS[i].x = -INPUTS[i].x;
+    ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan2, INPUTS[i],
+                                   LIBC_NAMESPACE::atan2f16(x, -y),
+                                   TOLERANCE + 0.5);
+  }
+}
+
+TEST_F(LlvmLibcAtan2f16Test, InFloat16Range) {
+  constexpr uint16_t X_START = FPBits(static_cast<float16>(0.25f)).uintval();
+  constexpr uint16_t X_STOP = FPBits(static_cast<float16>(4.0f)).uintval();
+  constexpr uint32_t X_COUNT = 1'23;
+  constexpr uint32_t X_STEP = (X_STOP - X_START) / X_COUNT;
+
+  constexpr uint16_t Y_START = FPBits(static_cast<float16>(0.25f)).uintval();
+  constexpr uint16_t Y_STOP = FPBits(static_cast<float16>(4.0f)).uintval();
+  constexpr uint32_t Y_COUNT = 1'37;
+  constexpr uint32_t Y_STEP = (Y_STOP - Y_START) / Y_COUNT;
+
+  auto test = [&](mpfr::RoundingMode rounding_mode) {
+    mpfr::ForceRoundingMode __r(rounding_mode);
+    if (!__r.success)
+      return;
+
+    uint64_t fails = 0;
+    uint64_t finite_count = 0;
+    uint64_t total_count = 0;
+    float16 failed_x = 0.0f, failed_y = 0.0f, failed_r = 0.0f;
+    double tol = 0.5;
+
+    for (uint32_t i = 0; i <= X_COUNT; ++i) {
+      uint16_t v = static_cast<uint16_t>(X_START + i * X_STEP);
+      float16 x = FPBits(v).get_val();
+      if (FPBits(v).is_nan() || FPBits(v).is_inf() || x < 0.0f)
+        continue;
+
+      for (uint32_t j = 0; j <= Y_COUNT; ++j) {
+        uint16_t w = static_cast<uint16_t>(Y_START + j * Y_STEP);
+        float16 y = FPBits(w).get_val();
+        if (FPBits(w).is_nan() || FPBits(w).is_inf())
+          continue;
+
+        libc_errno = 0;
+        float16 result = LIBC_NAMESPACE::atan2f16(x, y);
+        ++total_count;
+        if (FPBits(result).is_nan() || FPBits(result).is_inf())
+          continue;
+
+        ++finite_count;
+        mpfr::BinaryInput<float16> input{x, y};
+
+        if (!TEST_MPFR_MATCH_ROUNDING_SILENTLY(mpfr::Operation::Atan2, input,
+                                               result, 0.5, rounding_mode)) {
+          ++fails;
+          while (!TEST_MPFR_MATCH_ROUNDING_SILENTLY(
+              mpfr::Operation::Atan2, input, result, tol, rounding_mode)) {
+            failed_x = x;
+            failed_y = y;
+            failed_r = result;
+
+            if (tol > 1000.0)
+              break;
+
+            tol *= 2.0;
+          }
+        }
+      }
+    }
+    if (fails || (finite_count < total_count)) {
+      tlog << " Atan2f16 failed: " << fails << "/" << finite_count << "/"
+           << total_count << " tests.\n"
+           << "   Max ULPs is at most: " << static_cast<uint64_t>(tol) << ".\n";
+    }
+    if (fails) {
+      mpfr::BinaryInput<float16> input{failed_x, failed_y};
+      EXPECT_MPFR_MATCH(mpfr::Operation::Atan2, input, failed_r, 0.5,
+                        rounding_mode);
+    }
+  };
+
+  tlog << " Test Rounding To Nearest...\n";
+  test(mpfr::RoundingMode::Nearest);
+
+  tlog << " Test Rounding Downward...\n";
+  test(mpfr::RoundingMode::Downward);
+
+  tlog << " Test Rounding Upward...\n";
+  test(mpfr::RoundingMode::Upward);
+
+  tlog << " Test Rounding Toward Zero...\n";
+  test(mpfr::RoundingMode::TowardZero);
+}
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index 88028d90d2d50..b802b8828ec28 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -4872,6 +4872,17 @@ add_fp_unittest(
     libc.src.__support.FPUtil.fp_bits
 )
 
+add_fp_unittest(
+  atan2f16_test
+  SUITE
+    libc-math-smoke-tests
+  SRCS
+    atan2f16_test.cpp
+  DEPENDS
+    libc.src.math.atan2f16
+    libc.src.__support.FPUtil.fp_bits
+)
+
 add_fp_unittest(
   atan2_test
   SUITE
diff --git a/libc/test/src/math/smoke/atan2f16_test.cpp b/libc/test/src/math/smoke/atan2f16_test.cpp
new file mode 100644
index 0000000000000..dd2dd90240378
--- /dev/null
+++ b/libc/test/src/math/smoke/atan2f16_test.cpp
@@ -0,0 +1,62 @@
+//===-- Unittests for atan2f16 --------------------------------------------===//
+//
+// 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/__support/FPUtil/FPBits.h"
+#include "src/math/atan2f16.h"
+#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcAtan2f16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
+
+static constexpr float16 one =
+    LIBC_NAMESPACE::fputil::FPBits<float16>::one().get_val();
+static constexpr float16 neg_one =
+    LIBC_NAMESPACE::fputil::FPBits<float16>::one(LIBC_NAMESPACE::Sign::NEG)
+        .get_val();
+
+TEST_F(LlvmLibcAtan2f16Test, SpecialNumbers) {
+  EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::atan2f16(sNaN, sNaN),
+                              FE_INVALID);
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::atan2f16(sNaN, one),
+                              FE_INVALID);
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::atan2f16(one, sNaN),
+                              FE_INVALID);
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::atan2f16(aNaN, zero));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::atan2f16(one, aNaN));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(zero, LIBC_NAMESPACE::atan2f16(zero, zero));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(neg_zero, LIBC_NAMESPACE::atan2f16(neg_zero, zero));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(zero, LIBC_NAMESPACE::atan2f16(one, inf));
+  EXPECT_MATH_ERRNO(0);
+
+  EXPECT_FP_EQ(neg_zero, LIBC_NAMESPACE::atan2f16(neg_one, inf));
+  EXPECT_MATH_ERRNO(0);
+}
+
+TEST_F(LlvmLibcAtan2f16Test, InRange) {
+  // atan2(1, 1) = pi/4
+  EXPECT_FP_EQ(0x1.92p-1f, LIBC_NAMESPACE::atan2f16(one, one));
+  EXPECT_MATH_ERRNO(0);
+
+  // atan2(0, 1) = 0
+  EXPECT_FP_EQ(zero, LIBC_NAMESPACE::atan2f16(zero, one));
+  EXPECT_MATH_ERRNO(0);
+}
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index 9f10754cddd9c..d976c80a4f319 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -2915,6 +2915,22 @@ libc_support_library(
     ],
 )
 
+libc_support_library(
+    name = "__support_math_atan2f16",
+    hdrs = ["src/__support/math/atan2f16.h"],
+    deps = [
+        ":__support_common",
+        ":__support_fputil_cast",
+        ":__support_fputil_except_value_utils",
+        ":__support_fputil_fenv_impl",
+        ":__support_fputil_fp_bits",
+        ":__support_fputil_multiply_add",
+        ":__support_fputil_polyeval",
+        ":__support_macros_optimization",
+        ":llvm_libc_macros_float16_macros",
+    ],
+)
+
 libc_support_library(
     name = "__support_math_atanf",
     hdrs = ["src/__support/math/atanf.h"],
@@ -5776,6 +5792,11 @@ libc_math_function(
     ],
 )
 
+libc_math_function(
+    name = "atan2f16",
+    additional_deps = [":__support_math_atan2f16"],
+)
+
 libc_math_function(
     name = "atan2",
     additional_deps = [":__support_math_atan2"],
diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/math/smoke/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/math/smoke/BUILD.bazel
index 5b093dbdca7cb..637dc85227a97 100644
--- a/utils/bazel/llvm-project-overlay/libc/test/src/math/smoke/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/test/src/math/smoke/BUILD.bazel
@@ -26,6 +26,8 @@ math_test(name = "atan2")
 
 math_test(name = "atan2f")
 
+math_test(name = "atan2f16")
+
 math_test(name = "atan")
 
 math_test(name = "atanf")

>From 5909ce098caaafa87037fccef960c7b02371a1fc Mon Sep 17 00:00:00 2001
From: xinlongchen <xinlongchen at tencent.com>
Date: Fri, 27 Feb 2026 20:14:57 +0800
Subject: [PATCH 2/4] add exhaustive test

---
 libc/test/src/math/exhaustive/CMakeLists.txt  | 16 +++++
 .../src/math/exhaustive/atan2f16_test.cpp     | 59 +++++++++++++++++++
 2 files changed, 75 insertions(+)
 create mode 100644 libc/test/src/math/exhaustive/atan2f16_test.cpp

diff --git a/libc/test/src/math/exhaustive/CMakeLists.txt b/libc/test/src/math/exhaustive/CMakeLists.txt
index 877d653a26291..d8dca842626b1 100644
--- a/libc/test/src/math/exhaustive/CMakeLists.txt
+++ b/libc/test/src/math/exhaustive/CMakeLists.txt
@@ -393,6 +393,22 @@ add_fp_unittest(
     -lpthread
 )
 
+add_fp_unittest(
+  atan2f16_test
+  NO_RUN_POSTBUILD
+  NEED_MPFR
+  SUITE
+    libc_math_exhaustive_tests
+  SRCS
+    atan2f16_test.cpp
+  DEPENDS
+    .exhaustive_test
+    libc.src.math.atan2f16
+    libc.src.__support.FPUtil.fp_bits
+  LINK_LIBRARIES
+    -lpthread
+)
+
 add_fp_unittest(
   fmod_generic_impl_test
   NO_RUN_POSTBUILD
diff --git a/libc/test/src/math/exhaustive/atan2f16_test.cpp b/libc/test/src/math/exhaustive/atan2f16_test.cpp
new file mode 100644
index 0000000000000..9c6d1b7e793ce
--- /dev/null
+++ b/libc/test/src/math/exhaustive/atan2f16_test.cpp
@@ -0,0 +1,59 @@
+//===-- Exhaustive test for atan2f16 --------------------------------------===//
+//
+// 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 "exhaustive_test.h"
+#include "src/__support/FPUtil/FPBits.h"
+#include "src/math/atan2f16.h"
+#include "utils/MPFRWrapper/MPFRUtils.h"
+
+namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
+
+struct Atan2f16Checker : public virtual LIBC_NAMESPACE::testing::Test {
+  using FloatType = float16;
+  using FPBits = LIBC_NAMESPACE::fputil::FPBits<float16>;
+  using StorageType = uint16_t;
+
+  uint64_t check(uint16_t x_start, uint16_t x_stop, uint16_t y_start,
+                 uint16_t y_stop, mpfr::RoundingMode rounding) {
+    mpfr::ForceRoundingMode r(rounding);
+    if (!r.success)
+      return (x_stop > x_start) || (y_stop > y_start);
+    uint16_t xbits = x_start;
+    uint64_t failed = 0;
+    do {
+      float16 x = FPBits(xbits).get_val();
+      uint16_t ybits = y_start;
+      do {
+        float16 y = FPBits(ybits).get_val();
+        mpfr::BinaryInput<float16> input{y, x}; // atan2(y, x)
+        // Allow 1 ULP: atan2f16 is implemented with ~1 ULP accuracy.
+        bool correct = TEST_MPFR_MATCH_ROUNDING_SILENTLY(
+            mpfr::Operation::Atan2, input, LIBC_NAMESPACE::atan2f16(y, x), 1.5,
+            rounding);
+        failed += (!correct);
+        if (!correct) {
+          EXPECT_MPFR_MATCH_ROUNDING(mpfr::Operation::Atan2, input,
+                                     LIBC_NAMESPACE::atan2f16(y, x), 1.5,
+                                     rounding);
+        }
+      } while (ybits++ < y_stop);
+    } while (xbits++ < x_stop);
+    return failed;
+  }
+};
+
+using LlvmLibcAtan2f16ExhaustiveTest =
+    LlvmLibcExhaustiveMathTest<Atan2f16Checker, 1 << 8>;
+
+static constexpr uint16_t ALL_BITS_START = 0x0000U;
+static constexpr uint16_t ALL_BITS_STOP = 0xFC00U; // finite + inf, no NaN
+
+TEST_F(LlvmLibcAtan2f16ExhaustiveTest, AllInputs) {
+  test_full_range_all_roundings(ALL_BITS_START, ALL_BITS_STOP, ALL_BITS_START,
+                                ALL_BITS_STOP);
+}

>From 5edd8955e1a3e3f10ebbbafd64f20c86176061b6 Mon Sep 17 00:00:00 2001
From: xinlongchen <xinlongchen at tencent.com>
Date: Fri, 27 Feb 2026 23:40:00 +0800
Subject: [PATCH 3/4] fix the shared lib review comments

---
 libc/src/__support/math/CMakeLists.txt        |  4 +-
 libc/src/__support/math/atan2f16.h            | 45 ++++++++++---------
 libc/src/math/generic/atan2f16.cpp            |  2 +-
 libc/test/shared/CMakeLists.txt               |  1 +
 libc/test/shared/shared_math_test.cpp         |  1 +
 .../llvm-project-overlay/libc/BUILD.bazel     |  3 +-
 6 files changed, 31 insertions(+), 25 deletions(-)

diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt
index e5d12e71737c6..9334e901a93de 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -250,10 +250,12 @@ add_header_library(
   HDRS
     atan2f16.h
   DEPENDS
-    libc.src.__support.FPUtil.cast
+    libc.src.__support.CPP.optional
     libc.src.__support.FPUtil.fenv_impl
     libc.src.__support.FPUtil.fp_bits
     libc.src.__support.FPUtil.polyeval
+    libc.src.__support.FPUtil.cast
+    libc.src.__support.FPUtil.except_value_utils
     libc.src.__support.macros.optimization
     libc.include.llvm-libc-macros.float16_macros
 )
diff --git a/libc/src/__support/math/atan2f16.h b/libc/src/__support/math/atan2f16.h
index e9bf923c8ede2..d48bb4874c834 100644
--- a/libc/src/__support/math/atan2f16.h
+++ b/libc/src/__support/math/atan2f16.h
@@ -28,30 +28,32 @@ namespace math {
 namespace atan2f16_internal {
 
 #ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
+
 // (x_abs, y_abs) keys for exceptional atan2f16 inputs;
 // index i -> ExceptValues input.
-constexpr size_t N_ATAN2F16_EXCEPTS = 11;
-constexpr uint16_t ATAN2F16_EXCEPT_X[] = {0x37DA, 0x3A01, 0x3EFD, 0x4059,
-                                          0x40CD, 0x3D84, 0x38C2, 0x3814,
-                                          0x3596, 0x41B5, 0x3C62};
-constexpr uint16_t ATAN2F16_EXCEPT_Y[] = {0x3631, 0x3BFE, 0x398B, 0x3E2F,
-                                          0x4378, 0x354A, 0x3A93, 0x3C1F,
-                                          0x4189, 0x3CA3, 0x3EB3};
+LIBC_INLINE_VAR constexpr size_t N_ATAN2F16_EXCEPTS = 11;
+LIBC_INLINE_VAR constexpr uint16_t ATAN2F16_EXCEPT_X[] = {
+    0x37DA, 0x3A01, 0x3EFD, 0x4059, 0x40CD, 0x3D84,
+    0x38C2, 0x3814, 0x3596, 0x41B5, 0x3C62};
+LIBC_INLINE_VAR constexpr uint16_t ATAN2F16_EXCEPT_Y[] = {
+    0x3631, 0x3BFE, 0x398B, 0x3E2F, 0x4378, 0x354A,
+    0x3A93, 0x3C1F, 0x4189, 0x3CA3, 0x3EB3};
 
 // (input = index, RZ result, RU offset, RD offset, RN offset).
-constexpr fputil::ExceptValues<float16, N_ATAN2F16_EXCEPTS> ATAN2F16_EXCEPTS{{
-    {0, 0x3957, 1, 0, 0},
-    {1, 0x3B69, 1, 0, 0},
-    {2, 0x360A, 1, 0, 1},
-    {3, 0x38F2, 1, 0, 0},
-    {4, 0x3BFE, 1, 0, 1},
-    {5, 0x3387, 1, 0, 0},
-    {6, 0x3B8D, 1, 0, 1},
-    {7, 0x3C71, 1, 0, 1},
-    {8, 0x3DC7, 1, 0, 1},
-    {9, 0x362C, 1, 0, 1},
-    {10, 0x3BEE, 1, 0, 0},
-}};
+LIBC_INLINE_VAR constexpr fputil::ExceptValues<float16, N_ATAN2F16_EXCEPTS>
+    ATAN2F16_EXCEPTS{{
+        {0, 0x3957, 1, 0, 0},
+        {1, 0x3B69, 1, 0, 0},
+        {2, 0x360A, 1, 0, 1},
+        {3, 0x38F2, 1, 0, 0},
+        {4, 0x3BFE, 1, 0, 1},
+        {5, 0x3387, 1, 0, 0},
+        {6, 0x3B8D, 1, 0, 1},
+        {7, 0x3C71, 1, 0, 1},
+        {8, 0x3DC7, 1, 0, 1},
+        {9, 0x362C, 1, 0, 1},
+        {10, 0x3BEE, 1, 0, 0},
+    }};
 
 LIBC_INLINE constexpr cpp::optional<float16>
 lookup_atan2f16_except(uint16_t x_abs, uint16_t y_abs) {
@@ -61,7 +63,8 @@ lookup_atan2f16_except(uint16_t x_abs, uint16_t y_abs) {
   }
   return cpp::nullopt;
 }
-#endif
+
+#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS
 
 // atan(u) for u in (0, 1]: atan(u) = u * P(u^2).
 LIBC_INLINE float atan_u(float u) {
diff --git a/libc/src/math/generic/atan2f16.cpp b/libc/src/math/generic/atan2f16.cpp
index 142ad233b5a17..34d6cbeb0bf03 100644
--- a/libc/src/math/generic/atan2f16.cpp
+++ b/libc/src/math/generic/atan2f16.cpp
@@ -1,4 +1,4 @@
-//===-- Half-precision atan2f16 function ----------------------------------===//
+//===-- Half-precision atan2 function ---------------------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
diff --git a/libc/test/shared/CMakeLists.txt b/libc/test/shared/CMakeLists.txt
index 424e4180fc932..10a7f1f5dad8a 100644
--- a/libc/test/shared/CMakeLists.txt
+++ b/libc/test/shared/CMakeLists.txt
@@ -25,6 +25,7 @@ add_fp_unittest(
     libc.src.__support.math.atan2
     libc.src.__support.math.atan2f
     libc.src.__support.math.atan2f128
+    libc.src.__support.math.atan2f16
     libc.src.__support.math.atanf
     libc.src.__support.math.atanf16
     libc.src.__support.math.atanhf
diff --git a/libc/test/shared/shared_math_test.cpp b/libc/test/shared/shared_math_test.cpp
index 2758e3749910c..a1171181fb37f 100644
--- a/libc/test/shared/shared_math_test.cpp
+++ b/libc/test/shared/shared_math_test.cpp
@@ -24,6 +24,7 @@ TEST(LlvmLibcSharedMathTest, AllFloat16) {
   EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::asinf16(0.0f16));
   EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::asinhf16(0.0f16));
   EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::asinpif16(0.0f16));
+  EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::atan2f16(0.0f16, 0.0f16));
   EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::atanf16(0.0f16));
   EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::atanhf16(0.0f16));
   EXPECT_FP_EQ(0x1p+0f16, LIBC_NAMESPACE::shared::cosf16(0.0f16));
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index d976c80a4f319..ae9dd169c805a 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -2919,12 +2919,11 @@ libc_support_library(
     name = "__support_math_atan2f16",
     hdrs = ["src/__support/math/atan2f16.h"],
     deps = [
-        ":__support_common",
+        ":__support_cpp_optional",
         ":__support_fputil_cast",
         ":__support_fputil_except_value_utils",
         ":__support_fputil_fenv_impl",
         ":__support_fputil_fp_bits",
-        ":__support_fputil_multiply_add",
         ":__support_fputil_polyeval",
         ":__support_macros_optimization",
         ":llvm_libc_macros_float16_macros",

>From 6250e1061446ca43eb101f291cca5c0cdc157f82 Mon Sep 17 00:00:00 2001
From: xinlongchen <xinlongchen at tencent.com>
Date: Sat, 28 Feb 2026 11:12:15 +0800
Subject: [PATCH 4/4] using a look up table to implement

---
 libc/src/__support/math/CMakeLists.txt        |   5 +-
 libc/src/__support/math/atan2f16.h            | 138 ++++++------------
 .../src/math/exhaustive/atan2f16_test.cpp     |   5 +-
 .../llvm-project-overlay/libc/BUILD.bazel     |   5 +-
 4 files changed, 52 insertions(+), 101 deletions(-)

diff --git a/libc/src/__support/math/CMakeLists.txt b/libc/src/__support/math/CMakeLists.txt
index 9334e901a93de..4a45506010113 100644
--- a/libc/src/__support/math/CMakeLists.txt
+++ b/libc/src/__support/math/CMakeLists.txt
@@ -250,12 +250,11 @@ add_header_library(
   HDRS
     atan2f16.h
   DEPENDS
-    libc.src.__support.CPP.optional
+    .inv_trigf_utils
     libc.src.__support.FPUtil.fenv_impl
     libc.src.__support.FPUtil.fp_bits
-    libc.src.__support.FPUtil.polyeval
     libc.src.__support.FPUtil.cast
-    libc.src.__support.FPUtil.except_value_utils
+    libc.src.__support.FPUtil.nearest_integer
     libc.src.__support.macros.optimization
     libc.include.llvm-libc-macros.float16_macros
 )
diff --git a/libc/src/__support/math/atan2f16.h b/libc/src/__support/math/atan2f16.h
index d48bb4874c834..be3a67d0dc4fe 100644
--- a/libc/src/__support/math/atan2f16.h
+++ b/libc/src/__support/math/atan2f16.h
@@ -13,82 +13,31 @@
 
 #ifdef LIBC_TYPES_HAS_FLOAT16
 
-#include "src/__support/CPP/optional.h"
+#include "inv_trigf_utils.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/nearest_integer.h"
 #include "src/__support/macros/optimization.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
 namespace math {
 
-namespace atan2f16_internal {
-
-#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
-
-// (x_abs, y_abs) keys for exceptional atan2f16 inputs;
-// index i -> ExceptValues input.
-LIBC_INLINE_VAR constexpr size_t N_ATAN2F16_EXCEPTS = 11;
-LIBC_INLINE_VAR constexpr uint16_t ATAN2F16_EXCEPT_X[] = {
-    0x37DA, 0x3A01, 0x3EFD, 0x4059, 0x40CD, 0x3D84,
-    0x38C2, 0x3814, 0x3596, 0x41B5, 0x3C62};
-LIBC_INLINE_VAR constexpr uint16_t ATAN2F16_EXCEPT_Y[] = {
-    0x3631, 0x3BFE, 0x398B, 0x3E2F, 0x4378, 0x354A,
-    0x3A93, 0x3C1F, 0x4189, 0x3CA3, 0x3EB3};
-
-// (input = index, RZ result, RU offset, RD offset, RN offset).
-LIBC_INLINE_VAR constexpr fputil::ExceptValues<float16, N_ATAN2F16_EXCEPTS>
-    ATAN2F16_EXCEPTS{{
-        {0, 0x3957, 1, 0, 0},
-        {1, 0x3B69, 1, 0, 0},
-        {2, 0x360A, 1, 0, 1},
-        {3, 0x38F2, 1, 0, 0},
-        {4, 0x3BFE, 1, 0, 1},
-        {5, 0x3387, 1, 0, 0},
-        {6, 0x3B8D, 1, 0, 1},
-        {7, 0x3C71, 1, 0, 1},
-        {8, 0x3DC7, 1, 0, 1},
-        {9, 0x362C, 1, 0, 1},
-        {10, 0x3BEE, 1, 0, 0},
-    }};
-
-LIBC_INLINE constexpr cpp::optional<float16>
-lookup_atan2f16_except(uint16_t x_abs, uint16_t y_abs) {
-  for (size_t i = 0; i < N_ATAN2F16_EXCEPTS; ++i) {
-    if (x_abs == ATAN2F16_EXCEPT_X[i] && y_abs == ATAN2F16_EXCEPT_Y[i])
-      return ATAN2F16_EXCEPTS.lookup(static_cast<uint16_t>(i));
-  }
-  return cpp::nullopt;
-}
-
-#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS
-
-// atan(u) for u in (0, 1]: atan(u) = u * P(u^2).
-LIBC_INLINE float atan_u(float u) {
-  float u2 = u * u;
-  float p = fputil::polyeval(u2, 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 u * p;
-}
-
-} // namespace atan2f16_internal
-
 LIBC_INLINE float16 atan2f16(float16 y, float16 x) {
+  using namespace inv_trigf_utils_internal;
   using FPBits = fputil::FPBits<float16>;
-  constexpr float PI = 0x1.921fb6p1f;
-  constexpr float PI_OVER_2 = 0x1.921fb6p0f;
-  constexpr float PI_OVER_4 = 0x1.921fb6p-1f;
-  constexpr float THREE_PI_OVER_4 = 0x1.2d97c8p1f;
-  constexpr float IS_NEG[2] = {1.0f, -1.0f};
-
-  // const_term[x_sign][y_sign][recip]; recip = (|x| < |y|)
-  constexpr float CONST_TERM[2][2][2] = {
-      {{0.0f, -PI_OVER_2}, {0.0f, -PI_OVER_2}},
-      {{-PI, PI_OVER_2}, {-PI, PI_OVER_2}},
+
+  constexpr double IS_NEG[2] = {1.0, -1.0};
+  constexpr double PI = 0x1.921fb54442d18p+1;
+  constexpr double PI_OVER_2 = 0x1.921fb54442d18p+0;
+  constexpr double PI_OVER_4 = 0x1.921fb54442d18p-1;
+  constexpr double THREE_PI_OVER_4 = 0x1.2d97c7f3321d2p+1;
+
+  // const_term[x_sign][recip]; recip = (|x| < |y|)
+  constexpr double CONST_TERM[2][2] = {
+      {0.0, -PI_OVER_2},
+      {-PI, PI_OVER_2},
   };
 
   FPBits x_bits(x), y_bits(y);
@@ -103,44 +52,49 @@ LIBC_INLINE float16 atan2f16(float16 y, float16 x) {
 
   if (LIBC_UNLIKELY(max_abs >= 0x7c00U || min_abs == 0)) {
     if (x_bits.is_nan() || y_bits.is_nan()) {
-      if (FPBits(x).is_signaling_nan() || FPBits(y).is_signaling_nan())
+      if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
         fputil::raise_except_if_required(FE_INVALID);
       return FPBits::quiet_nan().get_val();
     }
-    float xf = static_cast<float>(x);
-    float yf = static_cast<float>(y);
-    size_t x_except = (xf == 0.0f) ? 0 : (x_abs == 0x7c00U ? 2 : 1);
-    size_t y_except = (yf == 0.0f) ? 0 : (y_abs == 0x7c00U ? 2 : 1);
-    constexpr float EXCEPTS[3][3][2] = {
-        {{0.0f, PI}, {0.0f, PI}, {0.0f, PI}},
-        {{PI_OVER_2, PI_OVER_2}, {0.0f, 0.0f}, {0.0f, PI}},
+    size_t x_except = (x_abs == 0) ? 0 : (x_abs == 0x7c00U ? 2 : 1);
+    size_t y_except = (y_abs == 0) ? 0 : (y_abs == 0x7c00U ? 2 : 1);
+    constexpr double EXCEPTS[3][3][2] = {
+        {{0.0, PI}, {0.0, PI}, {0.0, PI}},
+        {{PI_OVER_2, PI_OVER_2}, {0.0, 0.0}, {0.0, PI}},
         {{PI_OVER_2, PI_OVER_2},
          {PI_OVER_2, PI_OVER_2},
          {PI_OVER_4, THREE_PI_OVER_4}},
     };
-    size_t x_neg = x_sign ? 1 : 0;
-    float r = IS_NEG[y_sign] * EXCEPTS[y_except][x_except][x_neg];
+    double r = IS_NEG[y_sign] * EXCEPTS[y_except][x_except][x_sign];
     return fputil::cast<float16>(r);
   }
 
-#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
-  if (!x_sign && !y_sign) {
-    if (auto r = atan2f16_internal::lookup_atan2f16_except(x_abs, y_abs);
-        LIBC_UNLIKELY(r.has_value()))
-      return r.value();
-  }
-#endif
-
   bool recip = x_abs < y_abs;
-  float final_sign = IS_NEG[(x_sign != y_sign) != recip];
-  float const_term = CONST_TERM[x_sign][y_sign][recip];
-
-  float n = FPBits(min_abs).get_val();
-  float d = FPBits(max_abs).get_val();
-  float u = n / d;
-
-  float atan_u_val = atan2f16_internal::atan_u(u);
-  float r = final_sign * (const_term + atan_u_val);
+  double final_sign = IS_NEG[x_sign ^ y_sign ^ recip];
+  double const_term = CONST_TERM[x_sign][recip];
+
+  // atan2(y,x) = final_sign * (const_term + atan(n/d)),
+  //   where n = min(|x|,|y|), d = max(|x|,|y|), so 0 <= n/d <= 1.
+  //
+  // To compute atan(n/d), we use a lookup table with 16 equally-spaced knots:
+  //   idx = round(16 * n/d),  so  |n/d - idx/16| <= 1/32.
+  //   q_d = n/d - idx/16.
+  // Then by the atan addition formula:
+  //   atan(n/d) = atan(idx/16) + q_d * Q(q_d)
+  // where Q(q_d) approximates
+  //   [atan(idx/16 + q_d) - atan(idx/16)] / q_d
+  // via atan_eval(q_d, idx) from inv_trigf_utils.
+  double n = static_cast<double>(FPBits(min_abs).get_val());
+  double d = static_cast<double>(FPBits(max_abs).get_val());
+  double q_d = n / d;
+
+  double k_d = fputil::nearest_integer(q_d * 0x1.0p4);
+  int idx = static_cast<int>(k_d);
+  q_d = fputil::multiply_add(k_d, -0x1.0p-4, q_d);
+
+  double p = atan_eval(q_d, static_cast<unsigned>(idx));
+  double r = final_sign *
+             fputil::multiply_add(q_d, p, const_term + ATAN_K_OVER_16[idx]);
   return fputil::cast<float16>(r);
 }
 
diff --git a/libc/test/src/math/exhaustive/atan2f16_test.cpp b/libc/test/src/math/exhaustive/atan2f16_test.cpp
index 9c6d1b7e793ce..5b7f3ed0d88c6 100644
--- a/libc/test/src/math/exhaustive/atan2f16_test.cpp
+++ b/libc/test/src/math/exhaustive/atan2f16_test.cpp
@@ -31,14 +31,13 @@ struct Atan2f16Checker : public virtual LIBC_NAMESPACE::testing::Test {
       do {
         float16 y = FPBits(ybits).get_val();
         mpfr::BinaryInput<float16> input{y, x}; // atan2(y, x)
-        // Allow 1 ULP: atan2f16 is implemented with ~1 ULP accuracy.
         bool correct = TEST_MPFR_MATCH_ROUNDING_SILENTLY(
-            mpfr::Operation::Atan2, input, LIBC_NAMESPACE::atan2f16(y, x), 1.5,
+            mpfr::Operation::Atan2, input, LIBC_NAMESPACE::atan2f16(y, x), 0.5,
             rounding);
         failed += (!correct);
         if (!correct) {
           EXPECT_MPFR_MATCH_ROUNDING(mpfr::Operation::Atan2, input,
-                                     LIBC_NAMESPACE::atan2f16(y, x), 1.5,
+                                     LIBC_NAMESPACE::atan2f16(y, x), 0.5,
                                      rounding);
         }
       } while (ybits++ < y_stop);
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index ae9dd169c805a..9fc49d79a6b2b 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -2919,13 +2919,12 @@ libc_support_library(
     name = "__support_math_atan2f16",
     hdrs = ["src/__support/math/atan2f16.h"],
     deps = [
-        ":__support_cpp_optional",
         ":__support_fputil_cast",
-        ":__support_fputil_except_value_utils",
         ":__support_fputil_fenv_impl",
         ":__support_fputil_fp_bits",
-        ":__support_fputil_polyeval",
+        ":__support_fputil_nearest_integer",
         ":__support_macros_optimization",
+        ":__support_math_inv_trigf_utils",
         ":llvm_libc_macros_float16_macros",
     ],
 )



More information about the libc-commits mailing list