[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