blob: eefa9031770db8f8becf1bff6c06d8c439a0564a [file] [log] [blame]
//===-- Single-precision general exp function -----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC_MATH_GENERIC_EXPXF_H
#define LLVM_LIBC_SRC_MATH_GENERIC_EXPXF_H
#include "common_constants.h" // Lookup tables EXP_M
#include "math_utils.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/nearest_integer.h"
#include "src/__support/common.h"
#include <src/__support/FPUtil/NearestIntegerOperations.h>
#include <errno.h>
namespace __llvm_libc {
// The algorithm represents exp(x) as
// exp(x) = 2^(ln(2) * i) * 2^(ln(2) * j / NUM_P )) * exp(dx)
// where i integer value, j integer in range [-NUM_P/2, NUM_P/2).
// 2^(ln(2) * j / NUM_P )) is a table values: 1.0 + EXP_M
// exp(dx) calculates by taylor expansion.
// Inversion of ln(2). Multiplication by EXP_num_p due to sampling by 1 /
// EXP_num_p Precise value of the constant is not needed.
static constexpr double LN2_INV = 0x1.71547652b82fep+0 * EXP_num_p;
// LN2_HIGH + LN2_LOW = ln(2) with precision higher than double(ln(2))
// Minus sign is to use FMA directly.
static constexpr double LN2_HIGH = -0x1.62e42fefa0000p-1 / EXP_num_p;
static constexpr double LN2_LOW = -0x1.cf79abc9e3b3ap-40 / EXP_num_p;
struct exe_eval_result_t {
// exp(x) = 2^MULT_POWER2 * mult_exp * (r + 1.0)
// where
// MULT_POWER2 template parameter;
// mult_exp = 2^e;
// r in range [~-0.3, ~0.41]
double mult_exp;
double r;
};
// The function correctly calculates exp value with at least float precision
// in range not narrow than [-log(2^-150), 90]
template <int MULT_POWER2 = 0>
inline static exe_eval_result_t exp_eval(double x) {
double ps_dbl = fputil::nearest_integer(LN2_INV * x);
// Negative sign due to multiply_add optimization
double mult_e1, ml;
{
int ps =
static_cast<int>(ps_dbl) + (1 << (EXP_bits_p - 1)) +
((fputil::FPBits<double>::EXPONENT_BIAS + MULT_POWER2) << EXP_bits_p);
int table_index = ps & (EXP_num_p - 1);
fputil::FPBits<double> bs;
bs.set_unbiased_exponent(ps >> EXP_bits_p);
ml = EXP_2_POW[table_index];
mult_e1 = bs.get_val();
}
double dx = fputil::multiply_add(ps_dbl, LN2_LOW,
fputil::multiply_add(ps_dbl, LN2_HIGH, x));
// Taylor series coefficients
double pe = dx * fputil::polyeval(dx, 1.0, 0x1.0p-1, 0x1.5555555555555p-3,
0x1.5555555555555p-5, 0x1.1111111111111p-7,
0x1.6c16c16c16c17p-10);
double r = fputil::multiply_add(ml, pe, pe) + ml;
return {mult_e1, r};
}
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_MATH_GENERIC_EXPXF_H