xref: /aosp_15_r20/external/llvm-libc/src/stdfix/exphk.cpp (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
1*71db0c75SAndroid Build Coastguard Worker //===-- Implementation of exphk function ----------------------------------===//
2*71db0c75SAndroid Build Coastguard Worker //
3*71db0c75SAndroid Build Coastguard Worker // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*71db0c75SAndroid Build Coastguard Worker // See https://llvm.org/LICENSE.txt for license information.
5*71db0c75SAndroid Build Coastguard Worker // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*71db0c75SAndroid Build Coastguard Worker //
7*71db0c75SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
8*71db0c75SAndroid Build Coastguard Worker 
9*71db0c75SAndroid Build Coastguard Worker #include "exphk.h"
10*71db0c75SAndroid Build Coastguard Worker #include "src/__support/CPP/bit.h"
11*71db0c75SAndroid Build Coastguard Worker #include "src/__support/common.h"
12*71db0c75SAndroid Build Coastguard Worker #include "src/__support/fixed_point/fx_bits.h"
13*71db0c75SAndroid Build Coastguard Worker #include "src/__support/macros/config.h"
14*71db0c75SAndroid Build Coastguard Worker 
15*71db0c75SAndroid Build Coastguard Worker namespace LIBC_NAMESPACE_DECL {
16*71db0c75SAndroid Build Coastguard Worker 
17*71db0c75SAndroid Build Coastguard Worker namespace {
18*71db0c75SAndroid Build Coastguard Worker 
19*71db0c75SAndroid Build Coastguard Worker // Look up tables for exp(hi) and exp(mid).
20*71db0c75SAndroid Build Coastguard Worker // Generated with Sollya:
21*71db0c75SAndroid Build Coastguard Worker // > for i from 0 to 89 do {
22*71db0c75SAndroid Build Coastguard Worker //     hi = floor(i/8) - 5;
23*71db0c75SAndroid Build Coastguard Worker //     m = i/8 - floor(i/8) - 0.5;
24*71db0c75SAndroid Build Coastguard Worker //     e_hi = nearestint(exp(hi) * 2^7) * 2^-7;
25*71db0c75SAndroid Build Coastguard Worker //     e_mid = nearestint(exp(m) * 2^7) * 2^-7;
26*71db0c75SAndroid Build Coastguard Worker //     print(hi, e_hi, m, e_mid);
27*71db0c75SAndroid Build Coastguard Worker //   };
28*71db0c75SAndroid Build Coastguard Worker // Notice that when i = 88 and 89, e_hi will overflow short accum range.
29*71db0c75SAndroid Build Coastguard Worker static constexpr short accum EXP_HI[12] = {
30*71db0c75SAndroid Build Coastguard Worker     0x1.0p-7hk, 0x1.0p-6hk, 0x1.8p-5hk,  0x1.1p-3hk,  0x1.78p-2hk,  0x1.0p0hk,
31*71db0c75SAndroid Build Coastguard Worker     0x1.5cp1hk, 0x1.d9p2hk, 0x1.416p4hk, 0x1.b4dp5hk, 0x1.28d4p7hk, SACCUM_MAX,
32*71db0c75SAndroid Build Coastguard Worker };
33*71db0c75SAndroid Build Coastguard Worker 
34*71db0c75SAndroid Build Coastguard Worker static constexpr short accum EXP_MID[8] = {
35*71db0c75SAndroid Build Coastguard Worker     0x1.38p-1hk, 0x1.6p-1hk, 0x1.9p-1hk, 0x1.c4p-1hk,
36*71db0c75SAndroid Build Coastguard Worker     0x1.0p0hk,   0x1.22p0hk, 0x1.48p0hk, 0x1.74p0hk,
37*71db0c75SAndroid Build Coastguard Worker };
38*71db0c75SAndroid Build Coastguard Worker 
39*71db0c75SAndroid Build Coastguard Worker } // anonymous namespace
40*71db0c75SAndroid Build Coastguard Worker 
41*71db0c75SAndroid Build Coastguard Worker LLVM_LIBC_FUNCTION(short accum, exphk, (short accum x)) {
42*71db0c75SAndroid Build Coastguard Worker   using FXRep = fixed_point::FXRep<short accum>;
43*71db0c75SAndroid Build Coastguard Worker   using StorageType = typename FXRep::StorageType;
44*71db0c75SAndroid Build Coastguard Worker   // Output overflow
45*71db0c75SAndroid Build Coastguard Worker   if (LIBC_UNLIKELY(x >= 0x1.64p2hk))
46*71db0c75SAndroid Build Coastguard Worker     return FXRep::MAX();
47*71db0c75SAndroid Build Coastguard Worker   // Lower bound where exp(x) -> 0:
48*71db0c75SAndroid Build Coastguard Worker   //   floor(log(2^-8) * 2^7) * 2^-7
49*71db0c75SAndroid Build Coastguard Worker   if (LIBC_UNLIKELY(x <= -0x1.63p2hk))
50*71db0c75SAndroid Build Coastguard Worker     return FXRep::ZERO();
51*71db0c75SAndroid Build Coastguard Worker 
52*71db0c75SAndroid Build Coastguard Worker   // Current range of x:
53*71db0c75SAndroid Build Coastguard Worker   //   -0x1.628p2 <= x <= 0x1.638p2
54*71db0c75SAndroid Build Coastguard Worker   // Range reduction:
55*71db0c75SAndroid Build Coastguard Worker   //   x = hi + mid + lo,
56*71db0c75SAndroid Build Coastguard Worker   // where:
57*71db0c75SAndroid Build Coastguard Worker   //   hi is an integer
58*71db0c75SAndroid Build Coastguard Worker   //   mid * 2^3 is an integer
59*71db0c75SAndroid Build Coastguard Worker   //   |lo| <= 2^-4.
60*71db0c75SAndroid Build Coastguard Worker   // Then exp(x) = exp(hi + mid + lo) = exp(hi) * exp(mid) * exp(lo)
61*71db0c75SAndroid Build Coastguard Worker   //             ~ exp(hi) * exp(mid) * (1 + lo)
62*71db0c75SAndroid Build Coastguard Worker   // with relative errors < |lo|^2 <= 2^-8.
63*71db0c75SAndroid Build Coastguard Worker   //   exp(hi) and exp(mid) are extracted from small lookup tables.
64*71db0c75SAndroid Build Coastguard Worker 
65*71db0c75SAndroid Build Coastguard Worker   // Round-to-nearest 1/8, tie-to-(+Int):
66*71db0c75SAndroid Build Coastguard Worker   constexpr short accum ONE_SIXTEENTH = 0x1.0p-4hk;
67*71db0c75SAndroid Build Coastguard Worker   // x_rounded = floor(x + 1/16).
68*71db0c75SAndroid Build Coastguard Worker   short accum x_rounded = ((x + ONE_SIXTEENTH) >> (FXRep::FRACTION_LEN - 3))
69*71db0c75SAndroid Build Coastguard Worker                           << (FXRep::FRACTION_LEN - 3);
70*71db0c75SAndroid Build Coastguard Worker   short accum lo = x - x_rounded;
71*71db0c75SAndroid Build Coastguard Worker 
72*71db0c75SAndroid Build Coastguard Worker   // Range of x_rounded:
73*71db0c75SAndroid Build Coastguard Worker   //   x_rounded >= floor((-0x1.628p2 + 0x1.0p-4) * 2^3) * 2^-3
74*71db0c75SAndroid Build Coastguard Worker   //              = -0x1.6p2 = -5.5
75*71db0c75SAndroid Build Coastguard Worker   // To get the indices, we shift the values so that it start with 0.
76*71db0c75SAndroid Build Coastguard Worker   // Range of indices:  0 <= indices <= 89
77*71db0c75SAndroid Build Coastguard Worker   StorageType indices = cpp::bit_cast<StorageType>((x_rounded + 0x1.6p2hk) >>
78*71db0c75SAndroid Build Coastguard Worker                                                    (FXRep::FRACTION_LEN - 3));
79*71db0c75SAndroid Build Coastguard Worker   // So we have the following relation:
80*71db0c75SAndroid Build Coastguard Worker   //   indices = (hi + mid + 44/8) * 8
81*71db0c75SAndroid Build Coastguard Worker   // That implies:
82*71db0c75SAndroid Build Coastguard Worker   //   hi + mid = indices/8 - 5.5
83*71db0c75SAndroid Build Coastguard Worker   // So for lookup tables, we can use the upper 4 bits to get:
84*71db0c75SAndroid Build Coastguard Worker   //   exp( floor(indices / 8) - 5 )
85*71db0c75SAndroid Build Coastguard Worker   // and lower 3 bits for:
86*71db0c75SAndroid Build Coastguard Worker   //   exp( (indices - floor(indices)) - 0.5 )
87*71db0c75SAndroid Build Coastguard Worker   short accum exp_hi = EXP_HI[indices >> 3];
88*71db0c75SAndroid Build Coastguard Worker   short accum exp_mid = EXP_MID[indices & 0x7];
89*71db0c75SAndroid Build Coastguard Worker   // exp(x) ~ exp(hi) * exp(mid) * (1 + lo);
90*71db0c75SAndroid Build Coastguard Worker   return (exp_hi * (exp_mid * (0x1.0p0hk + lo)));
91*71db0c75SAndroid Build Coastguard Worker }
92*71db0c75SAndroid Build Coastguard Worker 
93*71db0c75SAndroid Build Coastguard Worker } // namespace LIBC_NAMESPACE_DECL
94