Skip to content

Commit fdd7c03

Browse files
authored
[libc][math][c23] Add tanhf16 C23 math function (#106006)
Part of #95250.
1 parent 852e477 commit fdd7c03

File tree

12 files changed

+400
-1
lines changed

12 files changed

+400
-1
lines changed

libc/config/gpu/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
587587
libc.src.math.setpayloadf16
588588
libc.src.math.setpayloadsigf16
589589
libc.src.math.sinhf16
590+
libc.src.math.tanhf16
590591
libc.src.math.totalorderf16
591592
libc.src.math.totalordermagf16
592593
libc.src.math.truncf16

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,7 @@ if(LIBC_TYPES_HAS_FLOAT16)
681681
libc.src.math.setpayloadsigf16
682682
libc.src.math.sinhf16
683683
libc.src.math.sinpif16
684+
libc.src.math.tanhf16
684685
libc.src.math.totalorderf16
685686
libc.src.math.totalordermagf16
686687
libc.src.math.truncf16

libc/docs/math/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ Higher Math Functions
348348
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
349349
| tan | |check| | |check| | | | | 7.12.4.7 | F.10.1.7 |
350350
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
351-
| tanh | |check| | | | | | 7.12.5.6 | F.10.2.6 |
351+
| tanh | |check| | | | |check| | | 7.12.5.6 | F.10.2.6 |
352352
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+
353353
| tanpi | | | | | | 7.12.4.14 | F.10.1.14 |
354354
+-----------+------------------+-----------------+------------------------+----------------------+------------------------+------------------------+----------------------------+

libc/spec/stdc.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,7 @@ def StdC : StandardSpec<"stdc"> {
798798
GuardedFunctionSpec<"sinhf16", RetValSpec<Float16Type>, [ArgSpec<Float16Type>], "LIBC_TYPES_HAS_FLOAT16">,
799799

800800
FunctionSpec<"tanhf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
801+
GuardedFunctionSpec<"tanhf16", RetValSpec<Float16Type>, [ArgSpec<Float16Type>], "LIBC_TYPES_HAS_FLOAT16">,
801802

802803
FunctionSpec<"acosf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
803804

libc/src/math/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ add_math_entrypoint_object(tanf)
496496

497497
add_math_entrypoint_object(tanh)
498498
add_math_entrypoint_object(tanhf)
499+
add_math_entrypoint_object(tanhf16)
499500

500501
add_math_entrypoint_object(tgamma)
501502
add_math_entrypoint_object(tgammaf)

libc/src/math/generic/CMakeLists.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4288,6 +4288,29 @@ add_entrypoint_object(
42884288
-O3
42894289
)
42904290

4291+
add_entrypoint_object(
4292+
tanhf16
4293+
SRCS
4294+
tanhf16.cpp
4295+
HDRS
4296+
../tanhf16.h
4297+
DEPENDS
4298+
.expxf16
4299+
libc.hdr.fenv_macros
4300+
libc.src.__support.CPP.array
4301+
libc.src.__support.FPUtil.cast
4302+
libc.src.__support.FPUtil.except_value_utils
4303+
libc.src.__support.FPUtil.fenv_impl
4304+
libc.src.__support.FPUtil.fp_bits
4305+
libc.src.__support.FPUtil.multiply_add
4306+
libc.src.__support.FPUtil.nearest_integer
4307+
libc.src.__support.FPUtil.polyeval
4308+
libc.src.__support.FPUtil.rounding_mode
4309+
libc.src.__support.macros.optimization
4310+
COMPILE_OPTIONS
4311+
-O3
4312+
)
4313+
42914314
add_entrypoint_object(
42924315
acoshf
42934316
SRCS

libc/src/math/generic/tanhf16.cpp

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//===-- Half-precision tanh(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/tanhf16.h"
10+
#include "expxf16.h"
11+
#include "hdr/fenv_macros.h"
12+
#include "src/__support/CPP/array.h"
13+
#include "src/__support/FPUtil/FEnvImpl.h"
14+
#include "src/__support/FPUtil/FPBits.h"
15+
#include "src/__support/FPUtil/PolyEval.h"
16+
#include "src/__support/FPUtil/cast.h"
17+
#include "src/__support/FPUtil/except_value_utils.h"
18+
#include "src/__support/FPUtil/multiply_add.h"
19+
#include "src/__support/FPUtil/nearest_integer.h"
20+
#include "src/__support/FPUtil/rounding_mode.h"
21+
#include "src/__support/common.h"
22+
#include "src/__support/macros/config.h"
23+
#include "src/__support/macros/optimization.h"
24+
25+
namespace LIBC_NAMESPACE_DECL {
26+
27+
static constexpr fputil::ExceptValues<float16, 2> TANHF16_EXCEPTS = {{
28+
// x = 0x1.f54p+0, tanhf16(x) = 0x1.ecp-1 (RZ)
29+
{0x3fd5U, 0x3bb0U, 1U, 0U, 0U},
30+
// x = -0x1.f54p+0, tanhf16(x) = -0x1.ecp-1 (RZ)
31+
{0xbfd5U, 0xbbb0U, 0U, 1U, 0U},
32+
}};
33+
34+
LLVM_LIBC_FUNCTION(float16, tanhf16, (float16 x)) {
35+
using FPBits = fputil::FPBits<float16>;
36+
FPBits x_bits(x);
37+
38+
uint16_t x_u = x_bits.uintval();
39+
uint16_t x_abs = x_u & 0x7fffU;
40+
41+
// When -2^(-14) <= x <= -2^(-9), or |x| <= 0x1.d2p-4,
42+
// or |x| >= atanh(1 - 2^(-11)), or x is NaN.
43+
if (LIBC_UNLIKELY(x_abs <= 0x2f48U || x_abs >= 0x4429U)) {
44+
// tanh(NaN) = NaN
45+
if (x_bits.is_nan()) {
46+
if (x_bits.is_signaling_nan()) {
47+
fputil::raise_except_if_required(FE_INVALID);
48+
return FPBits::quiet_nan().get_val();
49+
}
50+
51+
return x;
52+
}
53+
54+
// When -2^(-14) <= x <= -2^(-9).
55+
if (x_u >= 0x8400U && x_u <= 0x9800U) {
56+
switch (fputil::quick_get_round()) {
57+
case FE_TONEAREST:
58+
case FE_DOWNWARD:
59+
return x;
60+
default:
61+
return FPBits(static_cast<uint16_t>(x_u - 1U)).get_val();
62+
}
63+
}
64+
65+
// When |x| <= 0x1.d2p-4.
66+
if (x_abs <= 0x2f48U) {
67+
float xf = x;
68+
float xf_sq = xf * xf;
69+
// Degree-7 Taylor expansion generated by Sollya with the following
70+
// commands:
71+
// > taylor(tanh(x), 7, 0);
72+
// > display = hexadecimal;
73+
// > // For each coefficient:
74+
// > round(/* put coefficient here */, SG, RN);
75+
return fputil::cast<float16>(
76+
xf * fputil::polyeval(xf_sq, 0x1p+0f, -0x1.555556p-2f, 0x1.111112p-3f,
77+
-0x1.ba1ba2p-5f));
78+
}
79+
80+
// tanh(+/-inf) = +/-1
81+
if (x_bits.is_inf())
82+
return FPBits::one(x_bits.sign()).get_val();
83+
84+
// When |x| >= atanh(1 - 2^(-11)).
85+
fputil::raise_except_if_required(FE_INEXACT);
86+
87+
int rounding_mode = fputil::quick_get_round();
88+
if ((rounding_mode == FE_TONEAREST && x_abs >= 0x4482U) ||
89+
(rounding_mode == FE_UPWARD && x_bits.is_pos()) ||
90+
(rounding_mode == FE_DOWNWARD && x_bits.is_neg())) {
91+
return FPBits::one(x_bits.sign()).get_val();
92+
}
93+
if (x_bits.is_pos())
94+
return fputil::cast<float16>(0x1.ffcp-1);
95+
return fputil::cast<float16>(-0x1.ffcp-1);
96+
}
97+
98+
if (auto r = TANHF16_EXCEPTS.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
99+
return r.value();
100+
101+
// For atanh(-1 + 2^(-11)) < x < atanh(1 - 2^(-11)), to compute tanh(x), we
102+
// perform the following range reduction: find hi, mid, lo, such that:
103+
// x = (hi + mid) * log(2) * 0.5 + lo, in which
104+
// hi is an integer,
105+
// mid * 2^5 is an integer,
106+
// -2^(-5) <= lo < 2^(-5).
107+
// In particular,
108+
// hi + mid = round(x * log2(e) * 2 * 2^5) * 2^(-5).
109+
// Then,
110+
// tanh(x) = sinh(x)/cosh(x)
111+
// = (e^x - e^(-x)) / (e^x + e^(-x))
112+
// = (e^(2x) - 1) / (e^(2x) + 1)
113+
// = (2^(hi + mid) * e^(2*lo) - 1) / (2^(hi + mid) * e^(2*lo) + 1)
114+
// = (e^(2*lo) - 2^(-hi - mid)) / (e^(2*lo) + 2^(-hi - mid))
115+
// We store 2^(-mid) in the lookup table EXP2_MID_5_BITS, and compute
116+
// 2^(-hi - mid) by adding -hi to the exponent field of 2^(-mid).
117+
// e^lo is computed using a degree-3 minimax polynomial generated by Sollya.
118+
119+
float xf = x;
120+
float kf = fputil::nearest_integer(xf * (LOG2F_E * 2.0f * 0x1.0p+5f));
121+
int x_hi_mid = -static_cast<int>(kf);
122+
unsigned x_hi = static_cast<unsigned>(x_hi_mid) >> 5;
123+
unsigned x_mid = static_cast<unsigned>(x_hi_mid) & 0x1f;
124+
// lo = x - (hi + mid)
125+
// = round(x * log2(e) * 2 * 2^5) * log(2) * 0.5 * (-2^(-5)) + x
126+
float lo = fputil::multiply_add(kf, LOGF_2 * 0.5f * -0x1.0p-5f, xf);
127+
128+
uint32_t exp2_hi_mid_bits =
129+
EXP2_MID_5_BITS[x_mid] +
130+
static_cast<uint32_t>(x_hi << fputil::FPBits<float>::FRACTION_LEN);
131+
// exp2_hi_mid = 2^(-hi - mid)
132+
float exp2_hi_mid = fputil::FPBits<float>(exp2_hi_mid_bits).get_val();
133+
// Degree-3 minimax polynomial generated by Sollya with the following
134+
// commands:
135+
// > display = hexadecimal;
136+
// > P = fpminimax(expm1(2*x)/x, 2, [|SG...|], [-2^-5, 2^-5]);
137+
// > 1 + x * P;
138+
float exp_2lo =
139+
fputil::polyeval(lo, 0x1p+0f, 0x1p+1f, 0x1.001p+1f, 0x1.555ddep+0f);
140+
return fputil::cast<float16>((exp_2lo - exp2_hi_mid) /
141+
(exp_2lo + exp2_hi_mid));
142+
}
143+
144+
} // namespace LIBC_NAMESPACE_DECL

libc/src/math/tanhf16.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Implementation header for tanhf16 -----------------------*- 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_TANHF16_H
10+
#define LLVM_LIBC_SRC_MATH_TANHF16_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 tanhf16(float16 x);
18+
19+
} // namespace LIBC_NAMESPACE_DECL
20+
21+
#endif // LLVM_LIBC_SRC_MATH_TANHF16_H

libc/test/src/math/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1966,6 +1966,17 @@ add_fp_unittest(
19661966
libc.src.__support.FPUtil.fp_bits
19671967
)
19681968

1969+
add_fp_unittest(
1970+
tanhf16_test
1971+
NEED_MPFR
1972+
SUITE
1973+
libc-math-unittests
1974+
SRCS
1975+
tanhf16_test.cpp
1976+
DEPENDS
1977+
libc.src.math.tanhf16
1978+
)
1979+
19691980
add_fp_unittest(
19701981
atanhf_test
19711982
NEED_MPFR

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3778,6 +3778,19 @@ add_fp_unittest(
37783778
libc.src.__support.FPUtil.fp_bits
37793779
)
37803780

3781+
add_fp_unittest(
3782+
tanhf16_test
3783+
SUITE
3784+
libc-math-smoke-tests
3785+
SRCS
3786+
tanhf16_test.cpp
3787+
DEPENDS
3788+
libc.hdr.fenv_macros
3789+
libc.src.errno.errno
3790+
libc.src.math.tanhf16
3791+
libc.src.__support.FPUtil.cast
3792+
)
3793+
37813794
add_fp_unittest(
37823795
atanhf_test
37833796
SUITE

0 commit comments

Comments
 (0)