#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_DOUBLE_DOUBLE_H
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_DOUBLE_DOUBLE_H
#include "multiply_add.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/cpu_features.h"
#include "src/__support/number_pair.h"
namespace LIBC_NAMESPACE_DECL {
namespace fputil {
using DoubleDouble = LIBC_NAMESPACE::NumberPair<double>;
template <bool FAST2SUM = true>
LIBC_INLINE constexpr DoubleDouble exact_add(double a, double b) {
DoubleDouble r{0.0, 0.0};
if constexpr (FAST2SUM) {
r.hi = a + b;
double t = r.hi - a;
r.lo = b - t;
} else {
r.hi = a + b;
double t1 = r.hi - a;
double t2 = r.hi - t1;
double t3 = b - t1;
double t4 = a - t2;
r.lo = t3 + t4;
}
return r;
}
LIBC_INLINE constexpr DoubleDouble add(const DoubleDouble &a,
const DoubleDouble &b) {
DoubleDouble r = exact_add(a.hi, b.hi);
double lo = a.lo + b.lo;
return exact_add(r.hi, r.lo + lo);
}
LIBC_INLINE constexpr DoubleDouble add(const DoubleDouble &a, double b) {
DoubleDouble r = exact_add<false>(a.hi, b);
return exact_add(r.hi, r.lo + a.lo);
}
template <size_t N = 27> LIBC_INLINE constexpr DoubleDouble split(double a) {
DoubleDouble r{0.0, 0.0};
constexpr double CN = static_cast<double>(1 << N);
constexpr double C = CN + 1.0;
double t1 = C * a;
double t2 = a - t1;
r.hi = t1 + t2;
r.lo = a - r.hi;
return r;
}
template <bool NO_FMA_ALL_ROUNDINGS = false>
LIBC_INLINE DoubleDouble exact_mult(double a, double b) {
DoubleDouble r{0.0, 0.0};
#ifdef LIBC_TARGET_CPU_HAS_FMA
r.hi = a * b;
r.lo = fputil::multiply_add(a, b, -r.hi);
#else
DoubleDouble as = split(a);
DoubleDouble bs;
if constexpr (NO_FMA_ALL_ROUNDINGS)
bs = split<28>(b);
else
bs = split(b);
r.hi = a * b;
double t1 = as.hi * bs.hi - r.hi;
double t2 = as.hi * bs.lo + t1;
double t3 = as.lo * bs.hi + t2;
r.lo = as.lo * bs.lo + t3;
#endif
return r;
}
LIBC_INLINE DoubleDouble quick_mult(double a, const DoubleDouble &b) {
DoubleDouble r = exact_mult(a, b.hi);
r.lo = multiply_add(a, b.lo, r.lo);
return r;
}
template <bool NO_FMA_ALL_ROUNDINGS = false>
LIBC_INLINE DoubleDouble quick_mult(const DoubleDouble &a,
const DoubleDouble &b) {
DoubleDouble r = exact_mult<NO_FMA_ALL_ROUNDINGS>(a.hi, b.hi);
double t1 = multiply_add(a.hi, b.lo, r.lo);
double t2 = multiply_add(a.lo, b.hi, t1);
r.lo = t2;
return r;
}
template <>
LIBC_INLINE DoubleDouble multiply_add<DoubleDouble>(const DoubleDouble &a,
const DoubleDouble &b,
const DoubleDouble &c) {
return add(c, quick_mult(a, b));
}
LIBC_INLINE DoubleDouble div(const DoubleDouble &a, const DoubleDouble &b) {
DoubleDouble r;
double q = 1.0 / b.hi;
r.hi = a.hi * q;
#ifdef LIBC_TARGET_CPU_HAS_FMA
double e_hi = fputil::multiply_add(b.hi, -r.hi, a.hi);
double e_lo = fputil::multiply_add(b.lo, -r.hi, a.lo);
#else
DoubleDouble b_hi_r_hi = fputil::exact_mult<true>(b.hi, -r.hi);
DoubleDouble b_lo_r_hi = fputil::exact_mult<true>(b.lo, -r.hi);
double e_hi = (a.hi + b_hi_r_hi.hi) + b_hi_r_hi.lo;
double e_lo = (a.lo + b_lo_r_hi.hi) + b_lo_r_hi.lo;
#endif
r.lo = q * (e_hi + e_lo);
return r;
}
}
}
#endif