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

Xinlong Chen via libc-commits libc-commits at lists.llvm.org
Thu Feb 26 18:04:49 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] [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")



More information about the libc-commits mailing list