blob: 2782095cc5fd3d2000c86f5d9be16b4db3ef856f [file] [edit]
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Shared utilities for double-precision sinh and cosh functions.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_SINHCOSH_UTILS_H
#define LLVM_LIBC_SRC___SUPPORT_MATH_SINHCOSH_UTILS_H
#include "src/__support/CPP/bit.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/double_double.h"
#include "src/__support/FPUtil/fp_bitwise.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/common.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h"
#include "src/__support/macros/properties/cpu_features.h"
#include "src/__support/math/exp_utils.h"
#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
#include "src/__support/math/sinhcosh_accurate_128.h"
#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
namespace LIBC_NAMESPACE_DECL {
namespace math {
namespace double_eval {
#ifdef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
// Splitting -log(2) ~ hi + lo with 35-bit-precision hi part so that
// kd * MLOG_2_HI is exact for |kd| < 2^18:
// > hi = round(-log(2), 35, RN);
// > lo = round(-log(2) - hi, D, RN);
LIBC_INLINE_VAR constexpr double MLOG_2_HI = -0x1.62e42fefcp-1;
LIBC_INLINE_VAR constexpr double MLOG_2_LO = 0x1.c610ca86c3899p-37;
// Degree-4 minimax polynomials in u^2 for (cosh(u) - 1) / u^2 and
// (sinh(u) - u) / u^3 on [-log(2) / 2, log(2) / 2]:
// cosh(u) - 1 ~ u^2 * P_even(u^2)
// sinh(u) - u ~ u^3 * P_odd(u^2).
//
// Generated by Sollya with:
// > display = hexadecimal!;
// > prec = 500;
// > B = (log(2) / 2)^2;
// > P_even = fpminimax((cosh(sqrt(x)) - 1) / x, 4, [|D...|], [2^(-100), B]);
// > for i from 0 to 4 do print(coeff(P_even, i), ",");
// > dirtyinfnorm((cosh(sqrt(x)) - 1) - x * P_even, [0, B]);
// 0x1.c244...p-57 < 2^-56.
LIBC_INLINE_VAR constexpr double EXP_EVEN_N0_COEFFS[5] = {
0x1.0000000000001p-1, // ~ 1/2!
0x1.5555555553ce4p-5, // ~ 1/4!
0x1.6c16c178a71cep-10, // ~ 1/6!
0x1.a019b8c6ec68dp-16, // ~ 1/8!
0x1.2891c21cf0c03p-22, // ~ 1/10!
};
// > P_odd = fpminimax((sinh(sqrt(x)) / sqrt(x) - 1) / x, 4, [|D...|],
// [2^(-100), B]);
// > for i from 0 to 4 do print(coeff(P_odd, i), ",");
// > dirtyinfnorm((sinh(sqrt(x)) - sqrt(x)) - x * sqrt(x) * P_odd, [0, B]);
// 0x1.4882...p-62 < 2^-61.
LIBC_INLINE_VAR constexpr double EXP_ODD_N0_COEFFS[5] = {
0x1.5555555555556p-3, // ~ 1/3!
0x1.111111111070cp-7, // ~ 1/5!
0x1.a01a01a901652p-13, // ~ 1/7!
0x1.71de0a3fd7069p-19, // ~ 1/9!
0x1.af3effc3463e9p-26, // ~ 1/11!
};
// Tableless (N = 0) evaluation of:
// sinh(x) = (e^x - e^-x) / 2 (when is_sinh is true)
// cosh(x) = (e^x + e^-x) / 2 (when is_sinh is false, with x >= 0)
// for 2^-26 < |x| < 0x1.633ce8fb9f87ep+9 (sinh) or
// 2^-27 < x < 0x1.633ce8fb9f87ep+9 (cosh).
//
// Range reduction:
// Let |x| = k * log(2) + u, where:
// k = round(|x| * log2(e))
// |u| <= log(2) / 2
// then:
// e^|x| = 2^k * e^u
// e^-|x| = 2^-k * e^-u = 2^k * 2^(-2 * k) * e^-u.
//
// Factoring out 2^(k - 1) and separating e^(+/- u) = cosh(u) +/- sinh(u):
// (e^|x| -/+ e^-|x|) / 2 = 2^(k - 1) * (e^u + m_m * e^-u)
// = 2^(k - 1) * (s * cosh(u) + d * sinh(u))
// where:
// m_m = (-/+ 1) * 2^(-2 * k)
// s = 1 + m_m
// d = 1 - m_m.
//
// Key properties across the domain:
// - For |x| < 0x1.633ce8fb9f87ep+9, k is in [0, 1025] so k - 1 is in
// [-1, 1024]. When k = 1025, u < 0 keeps:
// r = e^u + m_m * e^-u < 1.0,
// so 2^1024 * r stays in the normal range and scale_exp2(r, k - 1) handles
// the entire domain without a separate top-octave branch.
//
// - For |x| < log(2) / 2, k = 0 gives u = |x| (exact, t2 = 0) and m_m = -/+ 1:
// * For sinh (m_m = -1): s = 0 and d = 2, reducing the evaluation to:
// 2^-1 * 2 * (u + u^3 * P_odd(u^2)) = x + x^3 * P_odd(x^2)
// with no cancellation down to |x| = 2^-26.
// * For cosh (m_m = +1): s = 2 and d = 0, reducing to:
// 1 + u^2 * P_even(u^2).
//
// - For 1 <= k <= 26, 2^(-2 * k) is in [2^-52, 2^-2], so both:
// s = 1.0 + m_m
// d = 1.0 - m_m
// are exact in all rounding modes. For k >= 27:
// |m_m| <= 2^-54 < 0.5 * ulp(1.0),
// so clamping m_m to 0.0 avoids a 1-ULP rounding error in directed rounding
// modes.
LIBC_MATH_SMALL_TABLES_INLINE double exp_pm_eval(double x, bool is_sinh) {
using FPBits = fputil::FPBits<double>;
FPBits xbits(x);
double x_a = xbits.abs().get_val();
// Using ROUND_BIAS, ROUND_SHIFT, and ROUND_MASK (see exp_utils.h), we compute
// k_biased = fma(x_a, LOG2_E.hi, ROUND_BIAS) and extract in parallel:
// kd = (k_biased & ROUND_MASK) - ROUND_SHIFT
// k = (k_bits >> 32) & 0x3'ffff.
double k_biased = fputil::multiply_add(x_a, LOG2_E.hi, ROUND_BIAS);
double kd = fputil::fp_and(k_biased, ROUND_MASK) - ROUND_SHIFT;
int k = static_cast<int>(cpp::bit_cast<uint64_t>(k_biased) >> 32) & 0x3'ffff;
// Secondary scale factor:
// m_m = (-/+ 1) * 2^(-2 * k) for k < 27
// m_m = 0.0 for k >= 27 (where 2^(-2 * k) <= 2^-54).
//
// Subtracting (1 << 11) = 0x800 (for sinh) sets bit 11 of the 12-bit
// two's-complement exponent field, flipping the sign bit of m_m when shifted
// left by 52 in scale_exp2:
int sign_shift = static_cast<int>(is_sinh) << 11;
int exp_diff = -2 * k - sign_shift;
double m_m = (k < 27) ? scale_exp2(1.0, exp_diff) : 0.0;
// Reduced argument:
// u = (x_a + kd * MLOG_2_HI) + kd * MLOG_2_LO.
// Since MLOG_2_HI has 35 significant bits and |kd| <= 1025,
// t1 = fma(kd, MLOG_2_HI, x_a)
// is exact in all rounding modes with or without FMA.
double t1 = fputil::multiply_add(kd, MLOG_2_HI, x_a); // exact
double t2 = kd * MLOG_2_LO;
double u = t1 + t2;
double u2 = u * u;
double u4 = u2 * u2;
// Evaluate degree-4 even and odd minimax polynomials P_even(u^2) and
// P_odd(u^2) in parallel using Estrin's scheme:
// cosh(u) ~ 1 + u^2 * P_even(u^2)
// sinh(u) ~ t1 + (t2 + u^3 * P_odd(u^2))
// First Estrin layer: c_0 + u^2 * c_1 and c_2 + u^2 * c_3
double e01 =
fputil::multiply_add(u2, EXP_EVEN_N0_COEFFS[1], EXP_EVEN_N0_COEFFS[0]);
double o01 =
fputil::multiply_add(u2, EXP_ODD_N0_COEFFS[1], EXP_ODD_N0_COEFFS[0]);
double e23 =
fputil::multiply_add(u2, EXP_EVEN_N0_COEFFS[3], EXP_EVEN_N0_COEFFS[2]);
double o23 =
fputil::multiply_add(u2, EXP_ODD_N0_COEFFS[3], EXP_ODD_N0_COEFFS[2]);
// Second Estrin layer: (c_2 + u^2 * c_3) + u^4 * c_4
double e24 = fputil::multiply_add(u4, EXP_EVEN_N0_COEFFS[4], e23);
double o24 = fputil::multiply_add(u4, EXP_ODD_N0_COEFFS[4], o23);
// Final polynomial values P_even(u^2) and P_odd(u^2):
double p_even = fputil::multiply_add(u4, e24, e01);
double p_odd = fputil::multiply_add(u4, o24, o01);
// Since m_m is either 0 or +/- 2^(-2 * k) with 0 <= k <= 26, both s and d are
// exact in all rounding modes:
double s = 1.0 + m_m; // exact
double d = 1.0 - m_m; // exact
// Apply sign(x) to the exact weights s and d before accumulating the
// polynomial so every subsequent rounding operation rounds in the active
// rounding direction for negative x as well (for cosh, the caller passes
// x >= 0 so sign_x == +0.0):
double sign_x = fputil::fp_xor(x, x_a);
s = fputil::fp_xor(s, sign_x);
d = fputil::fp_xor(d, sign_x);
// Accumulate from smallest to largest magnitude before adding the leading
// constant s. Adding s last avoids a double rounding at the leading ULP
// position of s when |d * t1| (< 2^-1) and |s * u^2 * P_even(u^2)| (< 2^-4)
// are added:
double d_t2 = d * t2;
double s_u2 = s * u2;
double d_u = d * u;
double d_u3 = d_u * u2;
// even_lo ~ s * u^2 * P_even(u^2) + d * t2
double even_lo = fputil::multiply_add(s_u2, p_even, d_t2);
// poly_lo ~ d * u^3 * P_odd(u^2) + even_lo
double poly_lo = fputil::multiply_add(d_u3, p_odd, even_lo);
// poly ~ d * t1 + poly_lo
double poly = fputil::multiply_add(d, t1, poly_lo);
// r ~ s * cosh(u) + d * sinh(u)
double r = s + poly;
return scale_exp2(r, k - 1);
}
#else // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
// Fixed-point splitting constants (ulp = 2^-22, 2^-29) for exact multiplication
// without Veltkamp splitting:
LIBC_INLINE_VAR constexpr double SPLIT_22 = 0x1.8p30; // 1.5 * 2^(-22 + 52)
LIBC_INLINE_VAR constexpr double SPLIT_29 = 0x1.8p23; // 1.5 * 2^(-29 + 52)
// Evaluate:
// sinh(x) = (e^x - e^-x) / 2 (when is_sinh is true)
// cosh(x) = (e^x + e^-x) / 2 (when is_sinh is false, with x >= 0)
// in double-double precision for 2^-2 <= |x| < 0x1.633ce8fb9f87ep+9 (sinh) or
// 2^-27 < x < 0x1.633ce8fb9f87ep+9 (cosh), falling back to
// exp_pm_accurate_128(x, is_sinh) when Ziv's rounding test fails.
//
// Range reduction:
// Let |x| = k_p * (log(2) / 2^7) + u
// in which:
// k_p = round(|x| * 2^7 * log2(e)), k_m = -k_p
// hi_p = k_p >> 7, idx_p = k_p & 0x7f
// hi_m = k_m >> 7, idx_m = k_m & 0x7f
// |u| <= 2^-8 * log(2)
// then:
// e^|x| = 2^hi_p * 2^(idx_p / 2^7) * e^u
// e^-|x| = 2^hi_m * 2^(idx_m / 2^7) * e^-u
// = 2^hi_p * (2^(hi_m - hi_p) * 2^(idx_m / 2^7)) * e^-u.
//
// Factoring out 2^(hi_p - 1):
// (e^|x| -/+ e^-|x|) / 2 = 2^(hi_p - 1) * (m_p * e^u + m_m * e^-u)
// where:
// m_p = 2^(idx_p / 2^7)
// m_m = (-/+ 1) * 2^(hi_m - hi_p) * 2^(idx_m / 2^7).
//
// For |x| < 0x1.633ce8fb9f87ep+9, hi_p is in [0, 1025] so hi_p - 1 is in
// [-1, 1024]. When hi_p == 1025, idx_p == 0 and u < 0, which keeps:
// r = m_p * e^u + m_m * e^-u < 1.0
// (biased exponent 1022). Adding hi_p - 1 = 1024 in scale_exp2(r, hi_p - 1)
// produces biased exponent 2046, handling the top octave
// [1024 * log(2), 1025 * log(2)) without a separate branch.
LIBC_MATH_SMALL_TABLES_INLINE double exp_pm_eval(double x, bool is_sinh) {
using FPBits = fputil::FPBits<double>;
FPBits xbits(x);
double x_a = xbits.abs().get_val();
constexpr double LOG2_E_SCALE = EXP_SCALE * LOG2_E.hi;
// Using ROUND_BIAS, ROUND_SHIFT, and ROUND_MASK (see exp_utils.h), we compute
// k_biased = fma(x_a, LOG2_E_SCALE, ROUND_BIAS) and extract in parallel:
// kd = (k_biased & ROUND_MASK) - ROUND_SHIFT
// k_p = (k_bits >> 32) & 0x3'ffff.
double k_biased = fputil::multiply_add(x_a, LOG2_E_SCALE, ROUND_BIAS);
double kd = fputil::fp_and(k_biased, ROUND_MASK) - ROUND_SHIFT;
int k_p =
static_cast<int>(cpp::bit_cast<uint64_t>(k_biased) >> 32) & 0x3'ffff;
int k_m = -k_p;
int hi_p = k_p >> EXP_N_BITS;
unsigned idx_p = static_cast<unsigned>(k_p & EXP_MASK);
int hi_m = k_m >> EXP_N_BITS;
unsigned idx_m = static_cast<unsigned>(k_m & EXP_MASK);
// Secondary exponent offset:
// exp_diff = hi_m - hi_p
// clamped at -100 (where e^-|x| <= 2^-99 * e^|x| << SINH_ERR_D) to avoid
// underflow in m_m:
int exp_diff = hi_m - hi_p;
int clamped_diff = (exp_diff > -100) ? exp_diff : -100;
// Subtracting (1 << 11) = 0x800 (for sinh) sets bit 11 of the 12-bit
// two's-complement exponent field, flipping the sign bit of m_m when shifted
// left by 52 in scale_exp2:
int sign_shift = static_cast<int>(is_sinh) << 11;
int scale_m = clamped_diff - sign_shift;
DoubleDouble m_p = EXP2_MID[idx_p];
// Scale m_m = EXP2_MID[idx_m] by (-/+ 1) * 2^clamped_diff:
// - Since m_m.hi is in [1, 2), we use scale_exp2(m_m.hi, scale_m) directly.
// - Since EXP2_MID[0].lo == 0.0, we scale m_m.lo by multiplying by:
// s_m = scale_exp2(1.0, scale_m).
double s_m = scale_exp2(1.0, scale_m);
DoubleDouble m_m = EXP2_MID[idx_m];
m_m.lo *= s_m;
m_m.hi = scale_exp2(m_m.hi, scale_m);
// Reduced argument:
// u = (x_a + kd * MLOG_2_EXP2_HI) + kd * MLOG_2_EXP2_MID.
// Since MLOG_2_EXP2_HI has 35 significant bits and |kd| < 2^18,
// t1 = fma(kd, MLOG_2_EXP2_HI, x_a)
// is exact in all rounding modes with or without FMA.
double t1 = fputil::multiply_add(kd, MLOG_2_EXP2_HI, x_a); // exact
double u = fputil::multiply_add(kd, MLOG_2_EXP2_MID, t1);
double u2 = u * u;
// Separate e^(+/-u) into even and odd parts:
// e^u ~ (1 + u^2 * P_even(u^2)) + (u + u^3 * P_odd(u^2))
// e^-u ~ (1 + u^2 * P_even(u^2)) - (u + u^3 * P_odd(u^2))
// where:
// P_even(u^2) = p_0 + p_2 * u^2
// P_odd(u^2) = p_1 + p_3 * u^2
// Then:
// m_p * e^u + m_m * e^-u
// ~ (m_p + m_m) * (1 + u^2 * P_even(u^2))
// + (m_p - m_m) * (u + u^3 * P_odd(u^2)).
double p_even = fputil::multiply_add(u2, EXP_P_COEFFS[2], EXP_P_COEFFS[0]);
double p_odd = fputil::multiply_add(u2, EXP_P_COEFFS[3], EXP_P_COEFFS[1]);
#ifdef LIBC_TARGET_CPU_HAS_FMA_DOUBLE
// Decompose m_p * e^u + m_m * e^-u ~ r_hi + r_lo:
// s.hi + s.lo = m_p.hi + m_m.hi (exact FastTwoSum)
// d.hi + d.lo = m_p.hi - m_m.hi (exact FastTwoSum)
// r_hi + r_err = s.hi + d.hi * t1
// r_lo ~ r_err + (s.lo + (m_p.lo - m_m.lo + d.lo) * t1)
// + ((m_p.lo + m_m.lo) + s.hi * u^2 * P_even(u^2))
// + d.hi * (kd * MLOG_2_EXP2_MID + u^3 * P_odd(u^2)).
//
// For |x| >= 2^-2, k_p >= 46 so hi_p >= 0 and hi_m <= -1, hence
// m_p.hi >= 1.0 > |m_m.hi|. By Sterbenz's lemma, both (s.hi - m_p.hi) and
// (m_p.hi - d.hi) are exact in all rounding modes.
DoubleDouble s = fputil::exact_add(m_p.hi, m_m.hi);
DoubleDouble d = fputil::exact_sub(m_p.hi, m_m.hi);
// Leading term r_hi = round(s.hi + d.hi * t1) and its rounding error r_err:
// Since |d.hi * t1| <= 2^-7 * log(2) < 2^-7 and s.hi > 2^-2, (s.hi - r_hi) is
// exact by Sterbenz's lemma.
double r_hi = fputil::multiply_add(d.hi, t1, s.hi);
double r_t = s.hi - r_hi; // exact
double r_err = fputil::multiply_add(d.hi, t1, r_t);
// Odd polynomial and low reduced-argument correction:
// odd_lo ~ (u - t1) + u^3 * P_odd(u^2) = kd * MLOG_2_EXP2_MID + u^3 * p_odd
double t2 = kd * MLOG_2_EXP2_MID;
double u3 = u * u2;
double odd_lo = fputil::multiply_add(u3, p_odd, t2);
// Combine table low parts and polynomial terms in parallel with the
// FastTwoSum residuals (s.lo, d.lo) and r_hi rounding error (r_err):
// mp_lo ~ m_p.lo * (1 + t1)
// mm_lo ~ m_m.lo * (1 - t1)
// even_lo ~ mp_lo + (s.hi * u^2) * P_even(u^2)
// poly_lo ~ even_lo + d.hi * odd_lo
double mp_lo = fputil::multiply_add(m_p.lo, t1, m_p.lo);
double mm_lo = fputil::multiply_add(-m_m.lo, t1, m_m.lo);
double s_u2 = s.hi * u2;
double even_lo = fputil::multiply_add(s_u2, p_even, mp_lo);
double poly_lo = fputil::multiply_add(d.hi, odd_lo, even_lo);
// Total linear error from FastTwoSum residuals and r_hi rounding:
// sd_lo ~ s.lo + d.lo * t1
// err_tot ~ r_err + sd_lo
double sd_lo = fputil::multiply_add(d.lo, t1, s.lo);
double err_tot = r_err + sd_lo;
// Apply sign(x) to r_hi, mm_lo, poly_lo, and err_tot before the final Ziv
// test additions so that (r_hi + lo_u) and (r_hi + lo_l) round in the active
// rounding direction for negative inputs (for cosh, the caller passes x >= 0
// so sign_x == +0.0):
double sign_x = fputil::fp_xor(x, x_a);
r_hi = fputil::fp_xor(r_hi, sign_x);
mm_lo = fputil::fp_xor(mm_lo, sign_x);
poly_lo = fputil::fp_xor(poly_lo, sign_x);
err_tot = fputil::fp_xor(err_tot, sign_x);
// Fold +/-SINH_ERR_D into mm_lo off the critical path so tail_{u,l} and
// err_tot finish in parallel:
constexpr double SINH_ERR_D = 0x1.4p-64;
double mm_lo_u = mm_lo + SINH_ERR_D;
double mm_lo_l = mm_lo - SINH_ERR_D;
double tail_u = poly_lo + mm_lo_u;
double tail_l = poly_lo + mm_lo_l;
double lo_u = err_tot + tail_u;
double lo_l = err_tot + tail_l;
#else // LIBC_TARGET_CPU_HAS_FMA_DOUBLE
// Fixed-point grid splitting for exact leading reconstruction in all rounding
// modes without FMA:
// lsb(mp_hi), lsb(mm_hi) >= 2^-22 => lsb(s_hi), lsb(d_hi) >= 2^-22
// lsb(u_hi) >= 2^-29
// Since s_hi = mp_hi + mm_hi > 2^-2, both s_hi, d_hi, and
// r_hi = s_hi + d_hi * u_hi (with lsb >= 2^-51)
// are exact in double.
// Split u = u_hi + u_lo with lsb(u_hi) >= 2^-29 and |u_lo| <= 2^-30:
double u_hi_t = t1 + SPLIT_29;
double u_hi = u_hi_t - SPLIT_29; // exact, lsb = 2^-29
double u_lo_t = t1 - u_hi; // exact
double u_lo = fputil::multiply_add(kd, MLOG_2_EXP2_MID, u_lo_t);
// Split m_p = mp_hi + mp_lo and m_m = mm_hi + mm_lo with lsb >= 2^-22:
double mp_hi_t = m_p.hi + SPLIT_22;
double mp_hi = mp_hi_t - SPLIT_22; // exact, lsb = 2^-22
double mp_lo_t = m_p.hi - mp_hi; // exact
double mp_lo = mp_lo_t + m_p.lo;
double mm_hi_t = m_m.hi + SPLIT_22;
double mm_hi = mm_hi_t - SPLIT_22; // exact, lsb = 2^-22
double mm_lo_t = m_m.hi - mm_hi; // exact
double mm_lo = mm_lo_t + m_m.lo;
// Exact leading sum, difference, and linear term r_hi = s_hi + d_hi * u_hi:
double s_hi = mp_hi + mm_hi; // exact, lsb = 2^-22
double d_hi = mp_hi - mm_hi; // exact, lsb = 2^-22
double r_hi = fputil::multiply_add(d_hi, u_hi, s_hi); // exact, lsb = 2^-51
// Low parts of the split table values and their linear contribution:
// s_lo = mp_lo + mm_lo, d_lo = mp_lo - mm_lo
// m_lo ~ s_lo + d_lo * u_hi
double s_lo = mp_lo + mm_lo;
double d_lo = mp_lo - mm_lo;
double m_lo = fputil::multiply_add(d_lo, u_hi, s_lo);
// Full-precision leading weights for the quadratic and cubic terms:
double s_m_hi = m_p.hi + m_m.hi;
double d_m_hi = m_p.hi - m_m.hi;
// Apply sign(x) before the final polynomial accumulation so that (r_hi +
// lo_u) and (r_hi + lo_l) round in the active rounding direction for negative
// x:
double sign_x = fputil::fp_xor(x, x_a);
r_hi = fputil::fp_xor(r_hi, sign_x);
s_m_hi = fputil::fp_xor(s_m_hi, sign_x);
d_m_hi = fputil::fp_xor(d_m_hi, sign_x);
m_lo = fputil::fp_xor(m_lo, sign_x);
// Combine even and odd polynomial terms with m_lo:
// even_lo ~ m_lo + s_m_hi * u^2 * P_even(u^2)
// odd_lo ~ u_lo + u^3 * P_odd(u^2)
// r_lo ~ even_lo + d_m_hi * odd_lo
double u2_peven = u2 * p_even;
double even_lo = fputil::multiply_add(s_m_hi, u2_peven, m_lo);
double u3 = u * u2;
double odd_lo = fputil::multiply_add(u3, p_odd, u_lo);
double r_lo = fputil::multiply_add(d_m_hi, odd_lo, even_lo);
// Total approximation and rounding error is bounded by SINH_ERR_D =
// 0x1.4p-64:
constexpr double SINH_ERR_D = 0x1.4p-64;
double lo_u = r_lo + SINH_ERR_D;
double lo_l = r_lo - SINH_ERR_D;
#endif // LIBC_TARGET_CPU_HAS_FMA_DOUBLE
// Ziv's rounding test in the current rounding mode:
double upper = r_hi + lo_u;
double lower = r_hi + lo_l;
if (LIBC_LIKELY(upper == lower))
return scale_exp2(upper, hi_p - 1);
return exp_pm_accurate_128(x, is_sinh);
}
#endif // LIBC_MATH_HAS_SKIP_ACCURATE_PASS
} // namespace double_eval
} // namespace math
} // namespace LIBC_NAMESPACE_DECL
#endif // LLVM_LIBC_SRC___SUPPORT_MATH_SINHCOSH_UTILS_H