Skip to content

Commit 573545c

Browse files
authored
[libc][math][c23] Add atanf16() function (#141612)
- Implementation of atan (tan inverse) function for 16-bit inputs. - Exhaustive tests across the 16-bit input range
1 parent 88aa5cb commit 573545c

File tree

11 files changed

+256
-2
lines changed

11 files changed

+256
-2
lines changed

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
673673
libc.src.math.acospif16
674674
libc.src.math.asinf16
675675
libc.src.math.asinhf16
676+
libc.src.math.atanf16
676677
libc.src.math.atanhf16
677678
libc.src.math.canonicalizef16
678679
libc.src.math.ceilf16

libc/docs/headers/math/index.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,13 +255,13 @@ Higher Math Functions
255255
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
256256
| acospi | | | | |check| | | 7.12.4.8 | F.10.1.8 |
257257
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
258-
| asin | |check| | |check| | | | | 7.12.4.2 | F.10.1.2 |
258+
| asin | |check| | |check| | | |check| | | 7.12.4.2 | F.10.1.2 |
259259
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
260260
| asinh | |check| | | | |check| | | 7.12.5.2 | F.10.2.2 |
261261
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
262262
| asinpi | | | | | | 7.12.4.9 | F.10.1.9 |
263263
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
264-
| atan | |check| | 1 ULP | | | | 7.12.4.3 | F.10.1.3 |
264+
| atan | |check| | 1 ULP | | |check| | | 7.12.4.3 | F.10.1.3 |
265265
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
266266
| atan2 | |check| | 1 ULP | | | 1 ULP | 7.12.4.4 | F.10.1.4 |
267267
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+

libc/include/math.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,13 @@ functions:
120120
return_type: float
121121
arguments:
122122
- type: float
123+
- name: atanf16
124+
standards:
125+
- stdc
126+
return_type: _Float16
127+
arguments:
128+
- type: _Float16
129+
guard: LIBC_TYPES_HAS_FLOAT16
123130
- name: atanhf
124131
standards:
125132
- stdc

libc/src/math/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ add_math_entrypoint_object(asinhf16)
6060

6161
add_math_entrypoint_object(atan)
6262
add_math_entrypoint_object(atanf)
63+
add_math_entrypoint_object(atanf16)
6364

6465
add_math_entrypoint_object(atan2)
6566
add_math_entrypoint_object(atan2f)

libc/src/math/atanf16.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Implementation header for atanf16 -----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception.
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_MATH_ATANF16_H
10+
#define LLVM_LIBC_SRC_MATH_ATANF16_H
11+
12+
#include "src/__support/macros/config.h"
13+
#include "src/__support/macros/properties/types.h"
14+
15+
namespace LIBC_NAMESPACE_DECL {
16+
17+
float16 atanf16(float16 x);
18+
19+
} // namespace LIBC_NAMESPACE_DECL
20+
21+
#endif // LLVM_LIBC_SRC_MATH_ATANF16_H

libc/src/math/generic/CMakeLists.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4234,6 +4234,26 @@ add_entrypoint_object(
42344234
libc.src.__support.macros.optimization
42354235
)
42364236

4237+
add_entrypoint_object(
4238+
atanf16
4239+
SRCS
4240+
atanf16.cpp
4241+
HDRS
4242+
../atanf16.h
4243+
DEPENDS
4244+
libc.hdr.errno_macros
4245+
libc.hdr.fenv_macros
4246+
libc.src.__support.FPUtil.cast
4247+
libc.src.__support.FPUtil.except_value_utils
4248+
libc.src.__support.FPUtil.fenv_impl
4249+
libc.src.__support.FPUtil.fp_bits
4250+
libc.src.__support.FPUtil.multiply_add
4251+
libc.src.__support.FPUtil.polyeval
4252+
libc.src.__support.FPUtil.sqrt
4253+
libc.src.__support.macros.optimization
4254+
libc.src.__support.macros.properties.types
4255+
)
4256+
42374257
add_entrypoint_object(
42384258
atan
42394259
SRCS

libc/src/math/generic/atanf16.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//===-- Half-precision atanf16(x) function --------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception.
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/math/atanf16.h"
10+
#include "hdr/errno_macros.h"
11+
#include "hdr/fenv_macros.h"
12+
#include "src/__support/FPUtil/FEnvImpl.h"
13+
#include "src/__support/FPUtil/FPBits.h"
14+
#include "src/__support/FPUtil/PolyEval.h"
15+
#include "src/__support/FPUtil/cast.h"
16+
#include "src/__support/FPUtil/except_value_utils.h"
17+
#include "src/__support/FPUtil/multiply_add.h"
18+
#include "src/__support/FPUtil/sqrt.h"
19+
#include "src/__support/macros/optimization.h"
20+
21+
namespace LIBC_NAMESPACE_DECL {
22+
23+
// Generated by Solly using the following command:
24+
// > round(pi/2, SG, RN);
25+
static constexpr float PI_2 = 0x1.921fb6p0;
26+
27+
#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
28+
static constexpr size_t N_EXCEPTS = 6;
29+
30+
static constexpr fputil::ExceptValues<float16, N_EXCEPTS> ATANF16_EXCEPTS{{
31+
// (input, RZ output, RU offset, RD offset, RN offset)
32+
{0x2745, 0x2744, 1, 0, 1},
33+
{0x3099, 0x3090, 1, 0, 1},
34+
{0x3c6c, 0x3aae, 1, 0, 1},
35+
{0x466e, 0x3daa, 1, 0, 1},
36+
{0x48ae, 0x3ddb, 1, 0, 0},
37+
{0x5619, 0x3e3d, 1, 0, 1},
38+
}};
39+
#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
40+
41+
LLVM_LIBC_FUNCTION(float16, atanf16, (float16 x)) {
42+
using FPBits = fputil::FPBits<float16>;
43+
FPBits xbits(x);
44+
45+
uint16_t x_u = xbits.uintval();
46+
uint16_t x_abs = x_u & 0x7fff;
47+
bool x_sign = x_u >> 15;
48+
float sign = (x_sign ? -1.0 : 1.0);
49+
50+
// |x| >= +/-inf
51+
if (LIBC_UNLIKELY(x_abs >= 0x7c00)) {
52+
if (xbits.is_nan()) {
53+
if (xbits.is_signaling_nan()) {
54+
fputil::raise_except_if_required(FE_INVALID);
55+
return FPBits::quiet_nan().get_val();
56+
}
57+
return x;
58+
}
59+
60+
// atanf16(+/-inf) = +/-pi/2
61+
return fputil::cast<float16>(sign * PI_2);
62+
}
63+
64+
float xf = x;
65+
float xsq = xf * xf;
66+
#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
67+
// Handle exceptional values
68+
if (auto r = ATANF16_EXCEPTS.lookup_odd(x_abs, x_sign);
69+
LIBC_UNLIKELY(r.has_value()))
70+
return r.value();
71+
#endif
72+
73+
// |x| <= 0x1p0, |x| <= 1
74+
if (x_abs <= 0x3c00) {
75+
// atanf16(+/-0) = +/-0
76+
if (LIBC_UNLIKELY(x_abs == 0))
77+
return x;
78+
79+
// Degree-14 minimax odd polynomial of atan(x) generated by Sollya with:
80+
// > P = fpminimax(atan(x)/x, [|0, 2, 4, 6, 8, 10, 12, 14|], [|SG...|],
81+
// [0, 1]);
82+
float result = fputil::polyeval(
83+
xsq, 0x1.fffffcp-1f, -0x1.55519ep-2f, 0x1.98f6a8p-3f, -0x1.1f0a92p-3f,
84+
0x1.95b654p-4f, -0x1.e65492p-5f, 0x1.8c0c36p-6f, -0x1.32316ep-8f);
85+
return fputil::cast<float16>(xf * result);
86+
}
87+
88+
// If |x| > 1
89+
// y = atan(x) = sign(x) * atan(|x|)
90+
// atan(|x|) = pi/2 - atan(1/|x|)
91+
// Recall, 1/|x| < 1
92+
float x_inv_sq = 1.0f / xsq;
93+
float x_inv = fputil::sqrt<float>(x_inv_sq);
94+
95+
// Degree-14 minimax odd polynomial of atan(x) generated by Sollya with:
96+
// > P = fpminimax(atan(x)/x, [|0, 2, 4, 6, 8, 10, 12, 14|], [|SG...|],
97+
// [0, 1]);
98+
float interm =
99+
fputil::polyeval(x_inv_sq, 0x1.fffffcp-1f, -0x1.55519ep-2f,
100+
0x1.98f6a8p-3f, -0x1.1f0a92p-3f, 0x1.95b654p-4f,
101+
-0x1.e65492p-5f, 0x1.8c0c36p-6f, -0x1.32316ep-8f);
102+
103+
return fputil::cast<float16>(sign *
104+
fputil::multiply_add(x_inv, -interm, PI_2));
105+
}
106+
107+
} // namespace LIBC_NAMESPACE_DECL

libc/test/src/math/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2338,6 +2338,17 @@ add_fp_unittest(
23382338
libc.src.__support.FPUtil.fp_bits
23392339
)
23402340

2341+
add_fp_unittest(
2342+
atanf16_test
2343+
NEED_MPFR
2344+
SUITE
2345+
libc-math-unittests
2346+
SRCS
2347+
atanf16_test.cpp
2348+
DEPENDS
2349+
libc.src.math.atanf16
2350+
)
2351+
23412352
add_fp_unittest(
23422353
scalbn_test
23432354
NEED_MPFR

libc/test/src/math/atanf16_test.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===-- Exhaustive test for atanf16 ---------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/math/atanf16.h"
10+
#include "test/UnitTest/FPMatcher.h"
11+
#include "test/UnitTest/Test.h"
12+
#include "utils/MPFRWrapper/MPFRUtils.h"
13+
14+
using LlvmLibcAtanf16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
15+
16+
namespace mpfr = LIBC_NAMESPACE::testing::mpfr;
17+
18+
// Range: [0, Inf]
19+
static constexpr uint16_t POS_START = 0x0000U;
20+
static constexpr uint16_t POS_STOP = 0x7c00U;
21+
22+
// Range: [-Inf, 0]
23+
static constexpr uint16_t NEG_START = 0x8000U;
24+
static constexpr uint16_t NEG_STOP = 0xfc00U;
25+
26+
TEST_F(LlvmLibcAtanf16Test, PositiveRange) {
27+
for (uint16_t v = POS_START; v <= POS_STOP; ++v) {
28+
float16 x = FPBits(v).get_val();
29+
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan, x,
30+
LIBC_NAMESPACE::atanf16(x), 0.5);
31+
}
32+
}
33+
34+
TEST_F(LlvmLibcAtanf16Test, NegativeRange) {
35+
for (uint16_t v = NEG_START; v <= NEG_STOP; ++v) {
36+
float16 x = FPBits(v).get_val();
37+
EXPECT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan, x,
38+
LIBC_NAMESPACE::atanf16(x), 0.5);
39+
}
40+
}

libc/test/src/math/smoke/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4107,6 +4107,17 @@ add_fp_unittest(
41074107
libc.src.math.atan
41084108
)
41094109

4110+
add_fp_unittest(
4111+
atanf16_test
4112+
SUITE
4113+
libc-math-smoke-tests
4114+
SRCS
4115+
atanf16_test.cpp
4116+
DEPENDS
4117+
libc.src.errno.errno
4118+
libc.src.math.atanf16
4119+
)
4120+
41104121
add_fp_unittest(
41114122
atan2f_test
41124123
SUITE
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//===-- Unittests for atanf16 ---------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception.
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/errno/libc_errno.h"
10+
#include "src/math/atanf16.h"
11+
#include "test/UnitTest/FPMatcher.h"
12+
#include "test/UnitTest/Test.h"
13+
14+
using LlvmLibcAtanf16Test = LIBC_NAMESPACE::testing::FPTest<float16>;
15+
16+
TEST_F(LlvmLibcAtanf16Test, SpecialNumbers) {
17+
LIBC_NAMESPACE::libc_errno = 0;
18+
EXPECT_FP_EQ(aNaN, LIBC_NAMESPACE::atanf16(aNaN));
19+
EXPECT_MATH_ERRNO(0);
20+
21+
EXPECT_FP_EQ_WITH_EXCEPTION(aNaN, LIBC_NAMESPACE::atanf16(sNaN), FE_INVALID);
22+
EXPECT_MATH_ERRNO(0);
23+
24+
EXPECT_FP_EQ(zero, LIBC_NAMESPACE::atanf16(zero));
25+
EXPECT_MATH_ERRNO(0);
26+
27+
EXPECT_FP_EQ(neg_zero, LIBC_NAMESPACE::atanf16(neg_zero));
28+
EXPECT_MATH_ERRNO(0);
29+
30+
EXPECT_FP_EQ(0x1.92p0, LIBC_NAMESPACE::atanf16(inf));
31+
EXPECT_MATH_ERRNO(0);
32+
33+
EXPECT_FP_EQ(-0x1.92p0, LIBC_NAMESPACE::atanf16(neg_inf));
34+
EXPECT_MATH_ERRNO(0);
35+
}

0 commit comments

Comments
 (0)