#include "src/math/exp10.h"
#include "common_constants.h"
#include "explogxf.h"
#include "src/__support/CPP/bit.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/FPUtil/FEnvImpl.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/double_double.h"
#include "src/__support/FPUtil/dyadic_float.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/FPUtil/nearest_integer.h"
#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/FPUtil/triple_double.h"
#include "src/__support/common.h"
#include "src/__support/integer_literals.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"
#include <errno.h>
namespace LIBC_NAMESPACE_DECL {
using fputil::DoubleDouble;
using fputil::TripleDouble;
using Float128 = typename fputil::DyadicFloat<128>;
using LIBC_NAMESPACE::operator""_u128;
constexpr double LOG2_10 = 0x1.a934f0979a371p+1;
constexpr double MLOG10_2_EXP2_M12_HI = -0x1.3441350ap-14;
constexpr double MLOG10_2_EXP2_M12_MID = 0x1.0c0219dc1da99p-51;
constexpr double MLOG10_2_EXP2_M12_MID_32 = 0x1.0c0219dcp-51;
constexpr double MLOG10_2_EXP2_M12_LO = 0x1.da994fd20dba2p-87;
constexpr double ERR_D = 0x1.8p-63;
constexpr double ERR_DD = 0x1.8p-99;
namespace {
LIBC_INLINE double poly_approx_d(double dx) {
double dx2 = dx * dx;
double c0 =
fputil::multiply_add(dx, 0x1.53524c73cea6ap+1, 0x1.26bb1bbb55516p+1);
double c1 =
fputil::multiply_add(dx, 0x1.2bd75cc6afc65p+0, 0x1.0470587aa264cp+1);
double p = fputil::multiply_add(dx2, c1, c0);
return p;
}
DoubleDouble poly_approx_dd(const DoubleDouble &dx) {
constexpr DoubleDouble COEFFS[] = {
{0, 0x1p0},
{-0x1.f48ad494e927bp-53, 0x1.26bb1bbb55516p1},
{-0x1.e2bfab3191cd2p-53, 0x1.53524c73cea69p1},
{0x1.80fb65ec3b503p-53, 0x1.0470591de2ca4p1},
{0x1.338fc05e21e55p-54, 0x1.2bd7609fd98c4p0},
{0x1.d4ea116818fbp-56, 0x1.1429ffd519865p-1},
{-0x1.872a8ff352077p-57, 0x1.a7ed70847c8b3p-3},
};
DoubleDouble p = fputil::polyeval(dx, COEFFS[0], COEFFS[1], COEFFS[2],
COEFFS[3], COEFFS[4], COEFFS[5], COEFFS[6]);
return p;
}
Float128 poly_approx_f128(const Float128 &dx) {
constexpr Float128 COEFFS_128[]{
{Sign::POS, -127, 0x80000000'00000000'00000000'00000000_u128},
{Sign::POS, -126, 0x935d8ddd'aaa8ac16'ea56d62b'82d30a2d_u128},
{Sign::POS, -126, 0xa9a92639'e753443a'80a99ce7'5f4d5bdb_u128},
{Sign::POS, -126, 0x82382c8e'f1652304'6a4f9d7d'bf6c9635_u128},
{Sign::POS, -124, 0x12bd7609'fd98c44c'34578701'9216c7af_u128},
{Sign::POS, -127, 0x450a7ff4'7535d889'cc41ed7e'0d27aee5_u128},
{Sign::POS, -130, 0xd3f6b844'702d636b'8326bb91'a6e7601d_u128},
{Sign::POS, -130, 0x45b937f0'd05bb1cd'fa7b46df'314112a9_u128},
};
Float128 p = fputil::polyeval(dx, COEFFS_128[0], COEFFS_128[1], COEFFS_128[2],
COEFFS_128[3], COEFFS_128[4], COEFFS_128[5],
COEFFS_128[6], COEFFS_128[7]);
return p;
}
Float128 exp10_f128(double x, double kd, int idx1, int idx2) {
double t1 = fputil::multiply_add(kd, MLOG10_2_EXP2_M12_HI, x);
double t2 = kd * MLOG10_2_EXP2_M12_MID_32;
double t3 = kd * MLOG10_2_EXP2_M12_LO;
Float128 dx = fputil::quick_add(
Float128(t1), fputil::quick_add(Float128(t2), Float128(t3)));
Float128 exp_mid1 =
fputil::quick_add(Float128(EXP2_MID1[idx1].hi),
fputil::quick_add(Float128(EXP2_MID1[idx1].mid),
Float128(EXP2_MID1[idx1].lo)));
Float128 exp_mid2 =
fputil::quick_add(Float128(EXP2_MID2[idx2].hi),
fputil::quick_add(Float128(EXP2_MID2[idx2].mid),
Float128(EXP2_MID2[idx2].lo)));
Float128 exp_mid = fputil::quick_mul(exp_mid1, exp_mid2);
Float128 p = poly_approx_f128(dx);
Float128 r = fputil::quick_mul(exp_mid, p);
r.exponent += static_cast<int>(kd) >> 12;
return r;
}
DoubleDouble exp10_double_double(double x, double kd,
const DoubleDouble &exp_mid) {
double t1 = fputil::multiply_add(kd, MLOG10_2_EXP2_M12_HI, x);
double t2 = kd * MLOG10_2_EXP2_M12_MID_32;
double t3 = kd * MLOG10_2_EXP2_M12_LO;
DoubleDouble dx = fputil::exact_add(t1, t2);
dx.lo += t3;
DoubleDouble p = poly_approx_dd(dx);
DoubleDouble r = fputil::quick_mult(exp_mid, p);
return r;
}
double exp10_denorm(double x) {
double tmp = fputil::multiply_add(x, LOG2_10, 0x1.8000'0000'4p21);
int k = static_cast<int>(cpp::bit_cast<uint64_t>(tmp) >> 19);
double kd = static_cast<double>(k);
uint32_t idx1 = (k >> 6) & 0x3f;
uint32_t idx2 = k & 0x3f;
int hi = k >> 12;
DoubleDouble exp_mid1{EXP2_MID1[idx1].mid, EXP2_MID1[idx1].hi};
DoubleDouble exp_mid2{EXP2_MID2[idx2].mid, EXP2_MID2[idx2].hi};
DoubleDouble exp_mid = fputil::quick_mult(exp_mid1, exp_mid2);
double lo_h = fputil::multiply_add(kd, MLOG10_2_EXP2_M12_HI, x);
double dx = fputil::multiply_add(kd, MLOG10_2_EXP2_M12_MID, lo_h);
double mid_lo = dx * exp_mid.hi;
double p = poly_approx_d(dx);
double lo = fputil::multiply_add(p, mid_lo, exp_mid.lo);
if (auto r = ziv_test_denorm(hi, exp_mid.hi, lo, ERR_D);
LIBC_LIKELY(r.has_value()))
return r.value();
DoubleDouble r_dd = exp10_double_double(x, kd, exp_mid);
if (auto r = ziv_test_denorm(hi, r_dd.hi, r_dd.lo, ERR_DD);
LIBC_LIKELY(r.has_value()))
return r.value();
Float128 r_f128 = exp10_f128(x, kd, idx1, idx2);
return static_cast<double>(r_f128);
}
double set_exceptional(double x) {
using FPBits = typename fputil::FPBits<double>;
FPBits xbits(x);
uint64_t x_u = xbits.uintval();
uint64_t x_abs = xbits.abs().uintval();
if (x_abs <= 0x3c8bcb7b1526e50e) {
return fputil::multiply_add(x, 0.5, 1.0);
}
if (x_u >= 0xc0733a7146f72a42) {
if (x_u > 0xc07439b746e36b52) {
if (xbits.is_inf())
return 0.0;
if (xbits.is_nan())
return x;
if (fputil::quick_get_round() == FE_UPWARD)
return FPBits::min_subnormal().get_val();
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_UNDERFLOW);
return 0.0;
}
return exp10_denorm(x);
}
if (x_u < 0x7ff0'0000'0000'0000ULL) {
int rounding = fputil::quick_get_round();
if (rounding == FE_DOWNWARD || rounding == FE_TOWARDZERO)
return FPBits::max_normal().get_val();
fputil::set_errno_if_required(ERANGE);
fputil::raise_except_if_required(FE_OVERFLOW);
}
return x + FPBits::inf().get_val();
}
}
LLVM_LIBC_FUNCTION(double, exp10, (double x)) {
using FPBits = typename fputil::FPBits<double>;
FPBits xbits(x);
uint64_t x_u = xbits.uintval();
if (LIBC_UNLIKELY(x_u >= 0xc0733a7146f72a42 ||
(x_u <= 0xbc7bcb7b1526e50e && x_u >= 0x40734413509f79ff) ||
x_u < 0x3c8bcb7b1526e50e)) {
return set_exceptional(x);
}
double tmp = fputil::multiply_add(x, LOG2_10, 0x1.8000'0000'4p21);
int k = static_cast<int>(cpp::bit_cast<uint64_t>(tmp) >> 19);
double kd = static_cast<double>(k);
uint32_t idx1 = (k >> 6) & 0x3f;
uint32_t idx2 = k & 0x3f;
int hi = k >> 12;
DoubleDouble exp_mid1{EXP2_MID1[idx1].mid, EXP2_MID1[idx1].hi};
DoubleDouble exp_mid2{EXP2_MID2[idx2].mid, EXP2_MID2[idx2].hi};
DoubleDouble exp_mid = fputil::quick_mult(exp_mid1, exp_mid2);
double lo_h = fputil::multiply_add(kd, MLOG10_2_EXP2_M12_HI, x);
double dx = fputil::multiply_add(kd, MLOG10_2_EXP2_M12_MID, lo_h);
double mid_lo = dx * exp_mid.hi;
double p = poly_approx_d(dx);
double lo = fputil::multiply_add(p, mid_lo, exp_mid.lo);
double upper = exp_mid.hi + (lo + ERR_D);
double lower = exp_mid.hi + (lo - ERR_D);
if (LIBC_LIKELY(upper == lower)) {
int64_t exp_hi = static_cast<int64_t>(hi) << FPBits::FRACTION_LEN;
double r = cpp::bit_cast<double>(exp_hi + cpp::bit_cast<int64_t>(upper));
return r;
}
if (LIBC_UNLIKELY((x_u & 0x8000'ffff'ffff'ffffULL) == 0ULL)) {
switch (x_u) {
case 0x3ff0000000000000:
return 10.0;
case 0x4000000000000000:
return 100.0;
case 0x4008000000000000:
return 1'000.0;
case 0x4010000000000000:
return 10'000.0;
case 0x4014000000000000:
return 100'000.0;
case 0x4018000000000000:
return 1'000'000.0;
case 0x401c000000000000:
return 10'000'000.0;
case 0x4020000000000000:
return 100'000'000.0;
case 0x4022000000000000:
return 1'000'000'000.0;
case 0x4024000000000000:
return 10'000'000'000.0;
case 0x4026000000000000:
return 100'000'000'000.0;
case 0x4028000000000000:
return 1'000'000'000'000.0;
case 0x402a000000000000:
return 10'000'000'000'000.0;
case 0x402c000000000000:
return 100'000'000'000'000.0;
case 0x402e000000000000:
return 1'000'000'000'000'000.0;
case 0x4030000000000000:
return 10'000'000'000'000'000.0;
case 0x4031000000000000:
return 100'000'000'000'000'000.0;
case 0x4032000000000000:
return 1'000'000'000'000'000'000.0;
case 0x4033000000000000:
return 10'000'000'000'000'000'000.0;
case 0x4034000000000000:
return 100'000'000'000'000'000'000.0;
case 0x4035000000000000:
return 1'000'000'000'000'000'000'000.0;
case 0x4036000000000000:
return 10'000'000'000'000'000'000'000.0;
case 0x4037000000000000:
return 0x1.52d02c7e14af6p76 + x;
}
}
DoubleDouble r_dd = exp10_double_double(x, kd, exp_mid);
double upper_dd = r_dd.hi + (r_dd.lo + ERR_DD);
double lower_dd = r_dd.hi + (r_dd.lo - ERR_DD);
if (LIBC_LIKELY(upper_dd == lower_dd)) {
int64_t exp_hi = static_cast<int64_t>(hi) << FPBits::FRACTION_LEN;
double r = cpp::bit_cast<double>(exp_hi + cpp::bit_cast<int64_t>(upper_dd));
return r;
}
Float128 r_f128 = exp10_f128(x, kd, idx1, idx2);
return static_cast<double>(r_f128);
}
}