40constexpr int kBase = 2148;
41constexpr int kTopBit = 1942 + 106 + kBase;
42constexpr int kChunks = kTopBit / 32 + 4;
43constexpr std::size_t kNormalizeEvery = std::size_t{1} << 30;
44constexpr uint64_t kMask32 = 0xffffffffULL;
52inline bool decode(
double v, Decoded &d) {
54 std::memcpy(&b, &v,
sizeof b);
55 const auto field =
static_cast<int>((b >> 52) & 0x7ff);
59 const uint64_t frac = b & ((uint64_t{1} << 52) - 1);
60 d.neg = (b >> 63) != 0;
65 d.m = frac | (uint64_t{1} << 52);
72inline void mul64(uint64_t a, uint64_t b, uint64_t &hi, uint64_t &lo) {
73#if defined(__SIZEOF_INT128__)
74 const unsigned __int128 p =
static_cast<unsigned __int128
>(a) * b;
75 lo =
static_cast<uint64_t
>(p);
76 hi =
static_cast<uint64_t
>(p >> 64);
77#elif defined(_MSC_VER) && defined(_M_X64)
78 lo = _umul128(a, b, &hi);
80 const uint64_t a0 = a & kMask32, a1 = a >> 32;
81 const uint64_t b0 = b & kMask32, b1 = b >> 32;
82 const uint64_t p00 = a0 * b0, p01 = a0 * b1, p10 = a1 * b0, p11 = a1 * b1;
83 const uint64_t mid = (p00 >> 32) + (p01 & kMask32) + (p10 & kMask32);
84 lo = (p00 & kMask32) | (mid << 32);
85 hi = p11 + (p01 >> 32) + (p10 >> 32) + (mid >> 32);
96 void add(uint64_t hi, uint64_t lo,
int e,
bool neg) {
100 const int pos = e + kBase;
101 const int c = pos >> 5;
102 const int s = pos & 31;
104 const uint64_t t0 = lo << s;
105 const uint64_t t1 = (hi << s) | (s != 0 ? lo >> (64 - s) : 0);
106 const uint64_t t2 = s != 0 ? hi >> (64 - s) : 0;
107 if (c < m_lo || c + 5 > m_hi) [[unlikely]] {
110 const int64_t sg = neg ? -1 : 1;
111 int64_t *w = m_w.data() + c;
112 w[0] += sg *
static_cast<int64_t
>(t0 & kMask32);
113 w[1] += sg *
static_cast<int64_t
>(t0 >> 32);
114 w[2] += sg *
static_cast<int64_t
>(t1 & kMask32);
115 w[3] += sg *
static_cast<int64_t
>(t1 >> 32);
116 w[4] += sg *
static_cast<int64_t
>(t2 & kMask32);
122 const int64_t sign = normalize();
124 m_w[
static_cast<std::size_t
>(m_hi - 1)] += sign * (int64_t{1} << 32);
130 void absorb(Accumulator &o) {
131 if (o.m_hi <= o.m_lo) {
136 if (o.m_lo < m_lo || o.m_hi > m_hi) {
137 clear(o.m_lo, o.m_hi);
139 for (
int k = o.m_lo; k < o.m_hi; ++k) {
140 m_w[
static_cast<std::size_t
>(k)] += o.m_w[
static_cast<std::size_t
>(k)];
145 const int64_t sign = normalize();
149 const bool neg = sign < 0;
154 for (
int k = m_lo; k < m_hi; ++k) {
155 const int64_t v = ((~m_w[k]) &
static_cast<int64_t
>(kMask32)) + carry;
156 m_w[k] = v &
static_cast<int64_t
>(kMask32);
160 m_w[
static_cast<std::size_t
>(m_hi)] = carry;
165 while (h >= m_lo && m_w[h] == 0) {
172 32 * h + 31 - std::countl_zero(
static_cast<uint32_t
>(m_w[h]));
175 if (lsb < kBase - 1074) {
178 uint64_t m = bits(lsb, tb - lsb + 1);
179 const bool half = bit(lsb - 1);
180 const bool sticky = below(lsb - 1);
181 if (half && (sticky || (m & 1U) != 0)) {
183 if (m == (uint64_t{1} << 53)) {
190 const double r = std::ldexp(
static_cast<double>(m), lsb - kBase);
197 int64_t normalize() {
203 for (; k < m_hi; ++k) {
204 const int64_t v = m_w[k] + carry;
205 m_w[k] = v &
static_cast<int64_t
>(kMask32);
210 while (carry != 0 && carry != -1) {
211 m_w[
static_cast<std::size_t
>(k)] = carry &
static_cast<int64_t
>(kMask32);
219 void clear(
int lo,
int hi) {
221 for (
int k = lo; k < hi; ++k) {
222 m_w[
static_cast<std::size_t
>(k)] = 0;
228 for (
int k = lo; k < m_lo; ++k) {
229 m_w[
static_cast<std::size_t
>(k)] = 0;
231 for (
int k = m_hi; k < hi; ++k) {
232 m_w[
static_cast<std::size_t
>(k)] = 0;
234 m_lo = lo < m_lo ? lo : m_lo;
235 m_hi = hi > m_hi ? hi : m_hi;
238 int64_t word(
int k)
const {
239 return (k >= m_lo && k < m_hi) ? m_w[
static_cast<std::size_t
>(k)] : 0;
242 bool bit(
int k)
const {
return ((word(k >> 5) >> (k & 31)) & 1) != 0; }
245 bool below(
int k)
const {
246 const int c = k >> 5;
247 for (
int j = m_lo; j < c && j < m_hi; ++j) {
248 if (m_w[
static_cast<std::size_t
>(j)] != 0) {
252 const uint64_t partMask = (uint64_t{1} << (k & 31)) - 1;
253 return (
static_cast<uint64_t
>(word(c)) & partMask) != 0;
257 uint64_t bits(
int k,
int width)
const {
258 const int c = k >> 5;
259 const int off = k & 31;
260 const uint64_t low =
static_cast<uint64_t
>(word(c)) |
261 (
static_cast<uint64_t
>(word(c + 1)) << 32);
262 const uint64_t high =
static_cast<uint64_t
>(word(c + 2));
263 const uint64_t v = (low >> off) | (off != 0 ? high << (64 - off) : 0);
264 return v & ((uint64_t{1} << width) - 1);
267 std::array<int64_t, kChunks> m_w;
285inline void twoSum(
double a,
double b,
double &s,
double &e) {
287 const double bb = s - a;
288 e = (a - (s - bb)) + (b - bb);
294inline void twoProduct(
double a,
double b,
double &p,
double &e) {
296 if constexpr (kFma) {
297 e = std::fma(a, b, -p);
299 constexpr double kSplitter = 134217729.0;
300 const double ca = kSplitter * a;
301 const double ah = ca - (ca - a);
302 const double al = a - ah;
303 const double cb = kSplitter * b;
304 const double bh = cb - (cb - b);
305 const double bl = b - bh;
306 e = ((ah * bh - p) + ah * bl + al * bh) + al * bl;
320#define EONC_EXACT_INLINE inline __attribute__((always_inline))
322#define EONC_EXACT_INLINE inline
327 std::size_t n,
double &out) {
328 constexpr double u = 0x1p-53;
329 if (n == 0 ||
static_cast<double>(n) > 0x1p30) {
332 double s[
kLanes] = {0.0, 0.0, 0.0, 0.0};
333 double c[
kLanes] = {0.0, 0.0, 0.0, 0.0};
334 double b[
kLanes] = {0.0, 0.0, 0.0, 0.0};
335 double maxAbs[
kLanes] = {0.0, 0.0, 0.0, 0.0};
336 double minProd[
kLanes] = {1.0, 1.0, 1.0, 1.0};
337 const auto term = [&](std::size_t k,
double xi,
double yi) {
340 twoProduct<kFma>(xi, yi, p, e);
342 twoSum(s[k], p, s[k], q);
344 b[k] += std::fabs(q) + std::fabs(e);
345 const double ax = std::fabs(xi);
346 const double ay = std::fabs(yi);
347 const double m = ax > ay ? ax : ay;
348 maxAbs[k] = m > maxAbs[k] ? m : maxAbs[k];
351 const double ap = std::fabs(p);
352 if (xi != 0.0 && yi != 0.0 && ap < minProd[k]) {
358 for (std::size_t k = 0; k <
kLanes; ++k) {
359 term(k, x[i + k], y[i + k]);
362 for (std::size_t k = 0; i + k < n; ++k) {
363 term(k, x[i + k], y[i + k]);
370 double mx = maxAbs[0];
371 double mp = minProd[0];
372 for (std::size_t k = 1; k <
kLanes; ++k) {
373 mx = maxAbs[k] > mx ? maxAbs[k] : mx;
374 mp = minProd[k] < mp ? minProd[k] : mp;
376 if (!(mx < 0x1p480) || !(mp >= 0x1p-960)) {
385 twoSum(s[0], s[1], s01, q1);
386 twoSum(s[2], s[3], s23, q2);
387 twoSum(s01, s23, sAll, q3);
388 const double cAll = ((c[0] + c[1]) + (c[2] + c[3])) + ((q1 + q2) + q3);
389 const double bAll = ((b[0] + b[1]) + (b[2] + b[3])) +
390 ((std::fabs(q1) + std::fabs(q2)) + std::fabs(q3));
395 const double N =
static_cast<double>(n) + 8.0;
396 const double gamma = (N * u) / (1.0 - N * u);
397 const double E = 2.0 * gamma * bAll + N * 0x1p-1070;
400 twoSum(sAll, cAll, r, d);
401 const double ar = std::fabs(r);
402 if (!(ar >= 0x1p-1000) || !(ar < 0x1p1000)) {
403 if (sAll == 0.0 && cAll == 0.0 && bAll == 0.0) {
411 const auto rb = std::bit_cast<uint64_t>(r);
412 const double above = std::bit_cast<double>(r > 0.0 ? rb + 1 : rb - 1);
413 const double below = std::bit_cast<double>(r > 0.0 ? rb - 1 : rb + 1);
414 const double hUp = 0.5 * (above - r);
415 const double hDn = 0.5 * (r - below);
416 constexpr double kMargin = 1.0 - 0x1p-30;
417 if (d + E < hUp * kMargin && d - E > -hDn * kMargin) {
424#if defined(__FMA__) || defined(__ARM_FEATURE_FMA) || defined(_M_ARM64)
425bool certifiedDot(
const double *x,
const double *y, std::size_t n,
427 return certifiedDotT<true>(x, y, n, out);
429#elif (defined(__x86_64__) || defined(__i386__)) && defined(__GNUC__)
431__attribute__((target(
"fma"))) bool
432certifiedDotFma(const
double *x, const
double *y, std::
size_t n,
double &out) {
433 return certifiedDotT<true>(x, y, n, out);
436bool certifiedDot(
const double *x,
const double *y, std::size_t n,
438 static const bool hasFma = __builtin_cpu_supports(
"fma");
439 return hasFma ? certifiedDotFma(x, y, n, out)
440 : certifiedDotT<false>(x, y, n, out);
443bool certifiedDot(
const double *x,
const double *y, std::size_t n,
445 return certifiedDotT<false>(x, y, n, out);
451double dot(
const double *x,
const double *y, std::size_t n) {
453 if (certifiedDot(x, y, n, certified)) {
456 std::array<Accumulator, kLanes> acc;
457 for (std::size_t i = 0; i < n; ++i) {
460 if (!decode(x[i], a) || !decode(y[i], b)) [[unlikely]] {
462 for (std::size_t j = 0; j < n; ++j) {
463 plain += x[j] * y[j];
469 mul64(a.m, b.m, hi, lo);
470 acc[i %
kLanes].add(hi, lo, a.e + b.e, a.neg != b.neg);
471 if ((i + 1) % kNormalizeEvery == 0) [[unlikely]] {
472 for (
auto &lane : acc) {
477 for (std::size_t k = 1; k <
kLanes; ++k) {
478 acc[0].absorb(acc[k]);
480 return acc[0].round();
483double orderedDot(
const double *x,
const double *y, std::size_t n) {
489 for (; i + 4 <= n; i += 4) {
490 const double p0 = x[i] * y[i];
491 const double p1 = x[i + 1] * y[i + 1];
492 const double p2 = x[i + 2] * y[i + 2];
493 const double p3 = x[i + 3] * y[i + 3];
500 const double p = x[i] * y[i];
504 const double p = x[i + 1] * y[i + 1];
508 const double p = x[i + 2] * y[i + 2];
511 return (l0 + l1) + (l2 + l3);
516double sumStrided(
const double *x, std::size_t n, std::size_t stride) {
518 for (std::size_t i = 0; i < n; ++i) {
520 if (!decode(x[i * stride], a)) [[unlikely]] {
522 for (std::size_t j = 0; j < n; ++j) {
523 plain += x[j * stride];
527 acc.add(0, a.m, a.e, a.neg);
528 if ((i + 1) % kNormalizeEvery == 0) [[unlikely]] {