xref: /llvm-project/libc/src/math/generic/expf16.cpp (revision 127349fcba81646389e4b8202b35405a5fdbef47)
1971a1ac4SOverMighty //===-- Half-precision e^x function ---------------------------------------===//
2971a1ac4SOverMighty //
3971a1ac4SOverMighty // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4971a1ac4SOverMighty // See https://llvm.org/LICENSE.txt for license information.
5971a1ac4SOverMighty // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6971a1ac4SOverMighty //
7971a1ac4SOverMighty //===----------------------------------------------------------------------===//
8971a1ac4SOverMighty 
9971a1ac4SOverMighty #include "src/math/expf16.h"
10560ed8ceSOverMighty #include "expxf16.h"
11971a1ac4SOverMighty #include "hdr/errno_macros.h"
12971a1ac4SOverMighty #include "hdr/fenv_macros.h"
13971a1ac4SOverMighty #include "src/__support/FPUtil/FEnvImpl.h"
14971a1ac4SOverMighty #include "src/__support/FPUtil/FPBits.h"
15971a1ac4SOverMighty #include "src/__support/FPUtil/PolyEval.h"
16*127349fcSOverMighty #include "src/__support/FPUtil/cast.h"
17971a1ac4SOverMighty #include "src/__support/FPUtil/except_value_utils.h"
18971a1ac4SOverMighty #include "src/__support/FPUtil/rounding_mode.h"
19971a1ac4SOverMighty #include "src/__support/common.h"
20971a1ac4SOverMighty #include "src/__support/macros/config.h"
21971a1ac4SOverMighty #include "src/__support/macros/optimization.h"
22971a1ac4SOverMighty 
23971a1ac4SOverMighty namespace LIBC_NAMESPACE_DECL {
24971a1ac4SOverMighty 
25971a1ac4SOverMighty static constexpr fputil::ExceptValues<float16, 2> EXPF16_EXCEPTS_LO = {{
26971a1ac4SOverMighty     // (input, RZ output, RU offset, RD offset, RN offset)
27971a1ac4SOverMighty     // x = 0x1.de4p-8, expf16(x) = 0x1.01cp+0 (RZ)
28971a1ac4SOverMighty     {0x1f79U, 0x3c07U, 1U, 0U, 0U},
29971a1ac4SOverMighty     // x = 0x1.73cp-6, expf16(x) = 0x1.05cp+0 (RZ)
30971a1ac4SOverMighty     {0x25cfU, 0x3c17U, 1U, 0U, 0U},
31971a1ac4SOverMighty }};
32971a1ac4SOverMighty 
33971a1ac4SOverMighty static constexpr fputil::ExceptValues<float16, 3> EXPF16_EXCEPTS_HI = {{
34971a1ac4SOverMighty     // (input, RZ output, RU offset, RD offset, RN offset)
35971a1ac4SOverMighty     // x = 0x1.c34p+0, expf16(x) = 0x1.74cp+2 (RZ)
36971a1ac4SOverMighty     {0x3f0dU, 0x45d3U, 1U, 0U, 1U},
37971a1ac4SOverMighty     // x = -0x1.488p-5, expf16(x) = 0x1.ebcp-1 (RZ)
38971a1ac4SOverMighty     {0xa922U, 0x3bafU, 1U, 0U, 0U},
39971a1ac4SOverMighty     // x = -0x1.55p-5, expf16(x) = 0x1.ebp-1 (RZ)
40971a1ac4SOverMighty     {0xa954U, 0x3bacU, 1U, 0U, 0U},
41971a1ac4SOverMighty }};
42971a1ac4SOverMighty 
43971a1ac4SOverMighty LLVM_LIBC_FUNCTION(float16, expf16, (float16 x)) {
44971a1ac4SOverMighty   using FPBits = fputil::FPBits<float16>;
45971a1ac4SOverMighty   FPBits x_bits(x);
46971a1ac4SOverMighty 
47971a1ac4SOverMighty   uint16_t x_u = x_bits.uintval();
48971a1ac4SOverMighty   uint16_t x_abs = x_u & 0x7fffU;
49971a1ac4SOverMighty 
50971a1ac4SOverMighty   // When 0 < |x| <= 2^(-5), or |x| >= 12, or x is NaN.
51971a1ac4SOverMighty   if (LIBC_UNLIKELY(x_abs <= 0x2800U || x_abs >= 0x4a00U)) {
52971a1ac4SOverMighty     // exp(NaN) = NaN
53971a1ac4SOverMighty     if (x_bits.is_nan()) {
54971a1ac4SOverMighty       if (x_bits.is_signaling_nan()) {
55971a1ac4SOverMighty         fputil::raise_except_if_required(FE_INVALID);
56971a1ac4SOverMighty         return FPBits::quiet_nan().get_val();
57971a1ac4SOverMighty       }
58971a1ac4SOverMighty 
59971a1ac4SOverMighty       return x;
60971a1ac4SOverMighty     }
61971a1ac4SOverMighty 
62971a1ac4SOverMighty     // When x >= 12.
63971a1ac4SOverMighty     if (x_bits.is_pos() && x_abs >= 0x4a00U) {
64971a1ac4SOverMighty       // exp(+inf) = +inf
65971a1ac4SOverMighty       if (x_bits.is_inf())
66971a1ac4SOverMighty         return FPBits::inf().get_val();
67971a1ac4SOverMighty 
68971a1ac4SOverMighty       switch (fputil::quick_get_round()) {
69971a1ac4SOverMighty       case FE_TONEAREST:
70971a1ac4SOverMighty       case FE_UPWARD:
71971a1ac4SOverMighty         fputil::set_errno_if_required(ERANGE);
72971a1ac4SOverMighty         fputil::raise_except_if_required(FE_OVERFLOW);
73971a1ac4SOverMighty         return FPBits::inf().get_val();
74971a1ac4SOverMighty       default:
75971a1ac4SOverMighty         return FPBits::max_normal().get_val();
76971a1ac4SOverMighty       }
77971a1ac4SOverMighty     }
78971a1ac4SOverMighty 
79971a1ac4SOverMighty     // When x <= -18.
80971a1ac4SOverMighty     if (x_u >= 0xcc80U) {
81971a1ac4SOverMighty       // exp(-inf) = +0
82971a1ac4SOverMighty       if (x_bits.is_inf())
83971a1ac4SOverMighty         return FPBits::zero().get_val();
84971a1ac4SOverMighty 
85971a1ac4SOverMighty       fputil::set_errno_if_required(ERANGE);
86971a1ac4SOverMighty       fputil::raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
87971a1ac4SOverMighty 
88971a1ac4SOverMighty       switch (fputil::quick_get_round()) {
89971a1ac4SOverMighty       case FE_UPWARD:
90971a1ac4SOverMighty         return FPBits::min_subnormal().get_val();
91971a1ac4SOverMighty       default:
92971a1ac4SOverMighty         return FPBits::zero().get_val();
93971a1ac4SOverMighty       }
94971a1ac4SOverMighty     }
95971a1ac4SOverMighty 
96971a1ac4SOverMighty     // When 0 < |x| <= 2^(-5).
97971a1ac4SOverMighty     if (x_abs <= 0x2800U && !x_bits.is_zero()) {
98971a1ac4SOverMighty       if (auto r = EXPF16_EXCEPTS_LO.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
99971a1ac4SOverMighty         return r.value();
100971a1ac4SOverMighty 
101971a1ac4SOverMighty       float xf = x;
102971a1ac4SOverMighty       // Degree-3 minimax polynomial generated by Sollya with the following
103971a1ac4SOverMighty       // commands:
104971a1ac4SOverMighty       //   > display = hexadecimal;
105971a1ac4SOverMighty       //   > P = fpminimax(expm1(x)/x, 2, [|SG...|], [-2^-5, 2^-5]);
106971a1ac4SOverMighty       //   > 1 + x * P;
107*127349fcSOverMighty       return fputil::cast<float16>(
108b66aa3bfSOverMighty           fputil::polyeval(xf, 0x1p+0f, 0x1p+0f, 0x1.0004p-1f, 0x1.555778p-3f));
109971a1ac4SOverMighty     }
110971a1ac4SOverMighty   }
111971a1ac4SOverMighty 
112971a1ac4SOverMighty   if (auto r = EXPF16_EXCEPTS_HI.lookup(x_u); LIBC_UNLIKELY(r.has_value()))
113971a1ac4SOverMighty     return r.value();
114971a1ac4SOverMighty 
115560ed8ceSOverMighty   // exp(x) = exp(hi + mid) * exp(lo)
116560ed8ceSOverMighty   auto [exp_hi_mid, exp_lo] = exp_range_reduction(x);
117*127349fcSOverMighty   return fputil::cast<float16>(exp_hi_mid * exp_lo);
118971a1ac4SOverMighty }
119971a1ac4SOverMighty 
120971a1ac4SOverMighty } // namespace LIBC_NAMESPACE_DECL
121