Eigen  5.0.1
 
Loading...
Searching...
No Matches
GenericPacketMathDoubleWord.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2018-2025 Rasmus Munk Larsen <rmlarsen@gmail.com>
5//
6// This Source Code Form is subject to the terms of the Mozilla
7// Public License v. 2.0. If a copy of the MPL was not distributed
8// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9// SPDX-License-Identifier: MPL-2.0
10
11#ifndef EIGEN_ARCH_GENERIC_PACKET_MATH_DOUBLE_WORD_H
12#define EIGEN_ARCH_GENERIC_PACKET_MATH_DOUBLE_WORD_H
13
14// IWYU pragma: private
15#include "../../InternalHeaderCheck.h"
16
17namespace Eigen {
18namespace internal {
19
20// This function splits x into the nearest integer n and fractional part r,
21// such that x = n + r holds exactly.
22template <typename Packet>
23EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void absolute_split(const Packet& x, Packet& n, Packet& r) {
24 n = pround(x);
25 r = psub(x, n);
26}
27
28// This function computes the sum {s_hi, s_lo}, such that x + y = s_hi + s_lo
29// holds exactly, and s_hi = fl(x+y), if |x| >= |y|.
30template <typename Packet>
31EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void fast_twosum(const Packet& x, const Packet& y, Packet& s_hi, Packet& s_lo) {
32 s_hi = padd(x, y);
33 const Packet t = psub(s_hi, x);
34 s_lo = psub(y, t);
35}
36
37#ifdef EIGEN_VECTORIZE_FMA
38// Given x, y, and xy = fl(x*y), return the residual such that x*y = xy + residual exactly.
39template <typename Packet, std::enable_if_t<!is_scalar<Packet>::value, int> = 0>
40EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet twoprod_low(const Packet& x, const Packet& y, const Packet& xy) {
41 return pmsub(x, y, xy);
42}
43
44template <typename Scalar, std::enable_if_t<is_scalar<Scalar>::value, int> = 0>
45EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar twoprod_low(const Scalar& x, const Scalar& y, const Scalar& xy) {
46 // Error-free products require FMA even when EIGEN_SCALAR_MADD_USE_FMA disables fusion in scalar madd.
47 return numext::fma(x, y, Scalar(-xy));
48}
49
50// This function implements the extended precision product of
51// a pair of floating point numbers. Given {x, y}, it computes the pair
52// {p_hi, p_lo} such that x * y = p_hi + p_lo holds exactly and
53// p_hi = fl(x * y).
54template <typename Packet>
55EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void twoprod(const Packet& x, const Packet& y, Packet& p_hi, Packet& p_lo) {
56 p_hi = pmul(x, y);
57 p_lo = twoprod_low(x, y, p_hi);
58}
59
60#else
61
62// Dekker's product requires separately rounded operations. GCC's C++ default -ffp-contract=fast may fuse
63// fl(x*y) into its consumers, so use fma for scalar types that <cmath> advertises as fast.
64// This does not prevent contraction in the remaining scalar or packet fallback paths.
65template <typename Scalar>
66struct has_fast_fma : std::false_type {};
67#ifdef FP_FAST_FMAF
68template <>
69struct has_fast_fma<float> : std::true_type {};
70#endif
71#ifdef FP_FAST_FMA
72template <>
73struct has_fast_fma<double> : std::true_type {};
74#endif
75#ifdef FP_FAST_FMAL
76template <>
77struct has_fast_fma<long double> : std::true_type {};
78#endif
79
80template <typename Scalar, std::enable_if_t<has_fast_fma<Scalar>::value, int> = 0>
81EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar twoprod_low(const Scalar& x, const Scalar& y, const Scalar& xy) {
82 return numext::fma(x, y, Scalar(-xy));
83}
84
85template <typename Scalar, std::enable_if_t<has_fast_fma<Scalar>::value, int> = 0>
86EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void twoprod(const Scalar& x, const Scalar& y, Scalar& p_hi, Scalar& p_lo) {
87 p_hi = x * y;
88 p_lo = twoprod_low(x, y, p_hi);
89}
90
91// This function implements the Veltkamp splitting. Given a floating point
92// number x it returns the pair {x_hi, x_lo} such that x_hi + x_lo = x holds
93// exactly and that half of the significand of x fits in x_hi.
94// This is Algorithm 3 from Jean-Michel Muller, "Elementary Functions",
95// 3rd edition, Birkh\"auser, 2016.
96template <typename Packet>
97EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void veltkamp_splitting(const Packet& x, Packet& x_hi, Packet& x_lo) {
98 using Scalar = typename unpacket_traits<Packet>::type;
99 constexpr int shift = (NumTraits<Scalar>::digits() + 1) / 2;
100 const Scalar shift_scale = Scalar(uint64_t(1) << shift); // Scalar constructor not necessarily constexpr.
101 const Packet gamma = pmul(pset1<Packet>(shift_scale + Scalar(1)), x);
102 Packet rho = psub(x, gamma);
103 x_hi = padd(rho, gamma);
104 x_lo = psub(x, x_hi);
105}
106
107// This function implements Dekker's algorithm for products x * y.
108// Given floating point numbers {x, y} computes the pair
109// {p_hi, p_lo} such that x * y = p_hi + p_lo holds exactly and
110// p_hi = fl(x * y).
111template <typename Packet, std::enable_if_t<!has_fast_fma<Packet>::value, int> = 0>
112EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void twoprod(const Packet& x, const Packet& y, Packet& p_hi, Packet& p_lo) {
113 Packet x_hi, x_lo, y_hi, y_lo;
114 veltkamp_splitting(x, x_hi, x_lo);
115 veltkamp_splitting(y, y_hi, y_lo);
116
117 p_hi = pmul(x, y);
118 p_lo = pmadd(x_hi, y_hi, pnegate(p_hi));
119 p_lo = pmadd(x_hi, y_lo, p_lo);
120 p_lo = pmadd(x_lo, y_hi, p_lo);
121 p_lo = pmadd(x_lo, y_lo, p_lo);
122}
123
124// A version of twoprod that takes x, y, and fl(x*y) as input and returns the p_lo such that
125// x * y = xy + p_lo holds exactly.
126template <typename Packet, std::enable_if_t<!has_fast_fma<Packet>::value, int> = 0>
127EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Packet twoprod_low(const Packet& x, const Packet& y, const Packet& xy) {
128 Packet x_hi, x_lo, y_hi, y_lo;
129 veltkamp_splitting(x, x_hi, x_lo);
130 veltkamp_splitting(y, y_hi, y_lo);
131
132 Packet p_lo = pmadd(x_hi, y_hi, pnegate(xy));
133 p_lo = pmadd(x_hi, y_lo, p_lo);
134 p_lo = pmadd(x_lo, y_hi, p_lo);
135 p_lo = pmadd(x_lo, y_lo, p_lo);
136 return p_lo;
137}
138
139#endif // EIGEN_VECTORIZE_FMA
140
141// This function implements the addition of two double word numbers
142// represented by {x_hi, x_lo} and {y_hi, y_lo}, with operands of either sign
143// and magnitude: Knuth's two-sum of the high parts is error-free, the low
144// parts are added to its error term, and the result is renormalized.
145// This is Algorithm 5 (SloppyDWPlusDW) from Joldes, Muller, & Popescu (2017),
146// "Tight and rigorous error bounds for basic building blocks of double-word
147// arithmetic", https://hal.science/hal-01351529: the error is a few u^2
148// relative to |x| + |y|, not to the sum, which may cancel.
149template <typename Packet>
150EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void twosum(const Packet& x_hi, const Packet& x_lo, const Packet& y_hi,
151 const Packet& y_lo, Packet& s_hi, Packet& s_lo) {
152 Packet s = padd(x_hi, y_hi);
153 Packet y_part = psub(s, x_hi);
154 Packet err = padd(psub(x_hi, psub(s, y_part)), psub(y_hi, y_part));
155 fast_twosum(s, padd(err, padd(x_lo, y_lo)), s_hi, s_lo);
156}
157
158// The difference {x_hi, x_lo} - {y_hi, y_lo} of two double word numbers,
159// the same algorithm as twosum with the sign of y folded into the operations.
160template <typename Packet>
161EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void twodiff(const Packet& x_hi, const Packet& x_lo, const Packet& y_hi,
162 const Packet& y_lo, Packet& s_hi, Packet& s_lo) {
163 Packet s = psub(x_hi, y_hi);
164 Packet y_part = psub(x_hi, s);
165 Packet err = psub(psub(x_hi, padd(s, y_part)), psub(y_hi, y_part));
166 fast_twosum(s, padd(err, psub(x_lo, y_lo)), s_hi, s_lo);
167}
168
169// This is a version of twosum for double word numbers,
170// which assumes that |x_hi| >= |y_hi|.
171template <typename Packet>
172EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void fast_twosum(const Packet& x_hi, const Packet& x_lo, const Packet& y_hi,
173 const Packet& y_lo, Packet& s_hi, Packet& s_lo) {
174 Packet r_hi, r_lo;
175 fast_twosum(x_hi, y_hi, r_hi, r_lo);
176 const Packet s = padd(padd(y_lo, r_lo), x_lo);
177 fast_twosum(r_hi, s, s_hi, s_lo);
178}
179
180// This is a version of twosum for adding a floating point number x to
181// double word number {y_hi, y_lo} number, with the assumption
182// that |x| >= |y_hi|.
183template <typename Packet>
184EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void fast_twosum(const Packet& x, const Packet& y_hi, const Packet& y_lo,
185 Packet& s_hi, Packet& s_lo) {
186 Packet r_hi, r_lo;
187 fast_twosum(x, y_hi, r_hi, r_lo);
188 const Packet s = padd(y_lo, r_lo);
189 fast_twosum(r_hi, s, s_hi, s_lo);
190}
191
192// This function implements the multiplication of a double word
193// number represented by {x_hi, x_lo} by a floating point number y.
194// It returns the result as a pair {p_hi, p_lo} such that
195// (x_hi + x_lo) * y = p_hi + p_lo holds with a relative error
196// of less than 2*2^{-2p}, where p is the number of significand bits
197// in the floating point type.
198// This is Algorithm 7 from Jean-Michel Muller, "Elementary Functions",
199// 3rd edition, Birkh\"auser, 2016.
200template <typename Packet>
201EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void twoprod(const Packet& x_hi, const Packet& x_lo, const Packet& y,
202 Packet& p_hi, Packet& p_lo) {
203 Packet c_hi, c_lo1;
204 twoprod(x_hi, y, c_hi, c_lo1);
205 const Packet c_lo2 = pmul(x_lo, y);
206 Packet t_hi, t_lo1;
207 fast_twosum(c_hi, c_lo2, t_hi, t_lo1);
208 const Packet t_lo2 = padd(t_lo1, c_lo1);
209 fast_twosum(t_hi, t_lo2, p_hi, p_lo);
210}
211
212// This function implements the multiplication of two double word
213// numbers represented by {x_hi, x_lo} and {y_hi, y_lo}.
214// For normalized inputs (x_hi = RN(x_hi + x_lo), likewise for y), the result
215// {p_hi, p_lo} has relative error < 5*u^2 for p-bit significands, u = 2^{-p}, p >= 8.
216// This assumes round-to-nearest, no overflow/underflow, an exact two-product
217// residual, and separately rounded operations outside that residual.
218// Each DW*FP product has relative error <= (3/2 + 4*u)*u^2: Joldes, Muller,
219// and Popescu (2017), "Tight and rigorous error bounds for basic building
220// blocks of double-word arithmetic", Theorem 4.1, https://hal.science/hal-01351529.
221// The final sum adds absolute error <= u^2*((3+3*u+u^2)*abs(p_hi_hi)
222// + (4+3*u+u^2)*abs(p_lo_hi)); using abs(y_lo) <= u*abs(y_hi) gives the bound above.
223template <typename Packet>
224EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void twoprod(const Packet& x_hi, const Packet& x_lo, const Packet& y_hi,
225 const Packet& y_lo, Packet& p_hi, Packet& p_lo) {
226 Packet p_hi_hi, p_hi_lo;
227 twoprod(x_hi, x_lo, y_hi, p_hi_hi, p_hi_lo);
228 Packet p_lo_hi, p_lo_lo;
229 twoprod(x_hi, x_lo, y_lo, p_lo_hi, p_lo_lo);
230 fast_twosum(p_hi_hi, p_hi_lo, p_lo_hi, p_lo_lo, p_hi, p_lo);
231}
232
233// This function implements the multiplication of two double word numbers
234// {x_hi, x_lo} and {y_hi, y_lo} at a lower cost than twoprod above: the
235// cross terms are accumulated onto the two-product residual and the x_lo*y_lo
236// term is dropped. For normalized inputs the relative error is < 7*u^2.
237// This is Algorithm 10 (DWTimesDW1) from Joldes, Muller, & Popescu (2017),
238// "Tight and rigorous error bounds for basic building blocks of double-word
239// arithmetic", https://hal.science/hal-01351529.
240template <typename Packet>
241EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void fast_twoprod(const Packet& x_hi, const Packet& x_lo, const Packet& y_hi,
242 const Packet& y_lo, Packet& p_hi, Packet& p_lo) {
243 Packet c_hi, c_lo;
244 twoprod(x_hi, y_hi, c_hi, c_lo);
245 c_lo = pmadd(x_hi, y_lo, pmadd(x_lo, y_hi, c_lo));
246 fast_twosum(c_hi, c_lo, p_hi, p_lo);
247}
248
249// This function implements the division of double word {x_hi, x_lo}
250// by float y. This is Algorithm 15 from "Tight and rigorous error bounds
251// for basic building blocks of double-word arithmetic", Joldes, Muller, & Popescu,
252// 2017. https://hal.archives-ouvertes.fr/hal-01351529
253template <typename Packet>
254EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void doubleword_div_fp(const Packet& x_hi, const Packet& x_lo, const Packet& y,
255 Packet& z_hi, Packet& z_lo) {
256 const Packet t_hi = pdiv(x_hi, y);
257 Packet pi_hi, pi_lo;
258 twoprod(t_hi, y, pi_hi, pi_lo);
259 const Packet delta_hi = psub(x_hi, pi_hi);
260 const Packet delta_t = psub(delta_hi, pi_lo);
261 const Packet delta = padd(delta_t, x_lo);
262 const Packet t_lo = pdiv(delta, y);
263 fast_twosum(t_hi, t_lo, z_hi, z_lo);
264}
265
266} // end namespace internal
267} // end namespace Eigen
268
269#endif // EIGEN_ARCH_GENERIC_PACKET_MATH_DOUBLE_WORD_H