1 //===-- Single-precision tanh function ------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "src/math/tanhf.h"
10 #include "src/__support/FPUtil/FPBits.h"
11 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
12 #include "src/__support/macros/properties/cpu_features.h" // LIBC_TARGET_CPU_HAS_FMA
13 #include "src/math/generic/explogxf.h"
15 namespace __llvm_libc
{
17 LLVM_LIBC_FUNCTION(float, tanhf
, (float x
)) {
18 using FPBits
= typename
fputil::FPBits
<float>;
20 bool sign
= xbits
.get_sign();
21 uint32_t x_abs
= xbits
.uintval() & FPBits::FloatProp::EXP_MANT_MASK
;
24 if (LIBC_UNLIKELY(x_abs
<= 0x3280'0000U
)) {
25 return static_cast<float>(
26 LIBC_UNLIKELY(x_abs
== 0) ? x
: (x
- 0x1.5555555555555p
-2 * x
* x
* x
));
29 // When |x| >= 15, or x is inf or nan
30 if (LIBC_UNLIKELY(x_abs
>= 0x4170'0000U
)) {
32 return x
+ 1.0f
; // sNaN to qNaN + signal
35 return sign
? -1.0f
: 1.0f
;
38 return -1.0f
+ opt_barrier(FPBits(FPBits::MIN_NORMAL
).get_val());
40 return 1.0f
- opt_barrier(FPBits(FPBits::MIN_NORMAL
).get_val());
44 if (LIBC_UNLIKELY(x_abs
<= 0x3da0'0000U
)) {
46 double x2
= xdbl
* xdbl
;
47 // Pure Taylor series.
48 double pe
= fputil::polyeval(x2
, 0.0, -0x1.5555555555555p
-2,
49 0x1.1111111111111p
-3, -0x1.ba1ba1ba1ba1cp
-5,
50 0x1.664f4882c10fap
-6, -0x1.226e355e6c23dp
-7);
51 return static_cast<float>(fputil::multiply_add(xdbl
, pe
, xdbl
));
54 if (LIBC_UNLIKELY(xbits
.bits
== 0x4058'e0a3U
)) {
55 if (fputil::get_round() == FE_DOWNWARD
)
56 return FPBits(0x3f7f'6ad9U
).get_val();
59 // Range reduction: e^(2x) = 2^(mid + hi) * e^lo
60 auto ep
= exp_b_range_reduc
<ExpBase
>(2.0f
* x
); // exp(2 * x)
61 double r
= ExpBase::powb_lo(ep
.lo
);
62 // tanh(x) = (exp(2x) - 1) / (exp(2x) + 1)
63 #if defined(LIBC_TARGET_CPU_HAS_FMA)
64 return static_cast<float>(fputil::multiply_add(ep
.mh
, r
, -1.0) /
65 fputil::multiply_add(ep
.mh
, r
, 1.0));
67 double exp_x
= ep
.mh
* r
;
68 return static_cast<float>((exp_x
- 1.0) / (exp_x
+ 1.0));
69 #endif // LIBC_TARGET_CPU_HAS_FMA
72 } // namespace __llvm_libc