[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