1use crate::env::{ExcFlags, FloatEnv, RoundMode};
5use crate::parts::{return_nan, round_pack, unpack, FloatClass, FloatParts};
6use crate::types::{
7 BFloat16, Float128, Float16, Float32, Float64, FloatFormat, FloatX80,
8};
9
10const INT_BIT: u32 = 126;
11
12pub fn fma<F: FloatFormat>(a: F, b: F, c: F, env: &mut FloatEnv) -> F {
14 let pa = unpack::<F>(a);
15 let pb = unpack::<F>(b);
16 let pc = unpack::<F>(c);
17 fma_parts::<F>(&pa, &pb, &pc, env)
18}
19
20fn fma_parts<F: FloatFormat>(
21 a: &FloatParts,
22 b: &FloatParts,
23 c: &FloatParts,
24 env: &mut FloatEnv,
25) -> F {
26 let ab_sign = a.sign ^ b.sign;
27
28 if a.is_nan() || b.is_nan() || c.is_nan() {
30 if a.cls == FloatClass::SNaN
32 || b.cls == FloatClass::SNaN
33 || c.cls == FloatClass::SNaN
34 {
35 env.raise(ExcFlags::INVALID);
36 }
37 if (a.cls == FloatClass::Inf && b.cls == FloatClass::Zero)
39 || (a.cls == FloatClass::Zero && b.cls == FloatClass::Inf)
40 {
41 env.raise(ExcFlags::INVALID);
42 }
43 let mut r = if a.is_nan() {
45 *a
46 } else if b.is_nan() {
47 *b
48 } else {
49 *c
50 };
51 if r.cls == FloatClass::SNaN {
52 r.cls = FloatClass::QNaN;
53 r.frac |= 1u128 << (INT_BIT - 1);
54 }
55 if env.default_nan() {
56 r = FloatParts::default_nan::<F>();
57 }
58 return round_pack::<F>(&mut r, env);
59 }
60
61 if (a.cls == FloatClass::Inf && b.cls == FloatClass::Zero)
63 || (a.cls == FloatClass::Zero && b.cls == FloatClass::Inf)
64 {
65 return return_nan::<F>(env);
66 }
67
68 if a.cls == FloatClass::Inf || b.cls == FloatClass::Inf {
70 if c.cls == FloatClass::Inf && c.sign != ab_sign {
71 return return_nan::<F>(env);
73 }
74 let mut r = FloatParts {
75 sign: ab_sign,
76 exp: 0,
77 frac: 0,
78 cls: FloatClass::Inf,
79 };
80 return round_pack::<F>(&mut r, env);
81 }
82
83 if c.cls == FloatClass::Inf {
85 let mut r = *c;
86 return round_pack::<F>(&mut r, env);
87 }
88
89 let ab_zero = a.cls == FloatClass::Zero || b.cls == FloatClass::Zero;
91
92 if ab_zero {
93 if c.cls == FloatClass::Zero {
94 let sign = if ab_sign == c.sign {
95 ab_sign
96 } else {
97 env.round_mode() == RoundMode::Down
98 };
99 let mut r = FloatParts {
100 sign,
101 exp: 0,
102 frac: 0,
103 cls: FloatClass::Zero,
104 };
105 return round_pack::<F>(&mut r, env);
106 }
107 let mut r = *c;
108 return round_pack::<F>(&mut r, env);
109 }
110
111 let p_exp = a.exp + b.exp;
113
114 let (p_hi, p_lo) = mul_u128(a.frac, b.frac);
116
117 let p_frac = (p_hi << 2) | (p_lo >> 126);
121 let p_sticky = if p_lo & ((1u128 << 126) - 1) != 0 {
122 1
123 } else {
124 0
125 };
126 let p_frac = p_frac | p_sticky;
127
128 if c.cls == FloatClass::Zero {
129 let mut r = FloatParts {
130 sign: ab_sign,
131 exp: p_exp,
132 frac: p_frac,
133 cls: FloatClass::Normal,
134 };
135 return round_pack::<F>(&mut r, env);
136 }
137
138 let product = FloatParts {
140 sign: ab_sign,
141 exp: p_exp,
142 frac: p_frac,
143 cls: FloatClass::Normal,
144 };
145
146 add_parts_fma::<F>(&product, c, env)
148}
149
150fn add_parts_fma<F: FloatFormat>(
152 a: &FloatParts,
153 b: &FloatParts,
154 env: &mut FloatEnv,
155) -> F {
156 if a.sign == b.sign {
157 let (mut big, mut small) =
159 if a.exp >= b.exp { (*a, *b) } else { (*b, *a) };
160 let exp_diff = (big.exp - small.exp) as u32;
161 if exp_diff > 0 {
162 if exp_diff >= 128 {
163 small.frac = if small.frac != 0 { 1 } else { 0 };
164 } else {
165 let sticky = if small.frac & ((1u128 << exp_diff) - 1) != 0 {
166 1u128
167 } else {
168 0
169 };
170 small.frac = (small.frac >> exp_diff) | sticky;
171 }
172 }
173 big.frac = big.frac.wrapping_add(small.frac);
174 let mut r = FloatParts {
175 sign: big.sign,
176 exp: big.exp,
177 frac: big.frac,
178 cls: FloatClass::Normal,
179 };
180 round_pack::<F>(&mut r, env)
181 } else {
182 let (big, small, sign) = if a.exp > b.exp {
184 (*a, *b, a.sign)
185 } else if a.exp < b.exp {
186 (*b, *a, b.sign)
187 } else if a.frac > b.frac {
188 (*a, *b, a.sign)
189 } else if a.frac < b.frac {
190 (*b, *a, b.sign)
191 } else {
192 let sign = env.round_mode() == RoundMode::Down;
193 let mut r = FloatParts {
194 sign,
195 exp: 0,
196 frac: 0,
197 cls: FloatClass::Zero,
198 };
199 return round_pack::<F>(&mut r, env);
200 };
201
202 let exp_diff = (big.exp - small.exp) as u32;
203 let mut small_frac = small.frac;
204 if exp_diff > 0 {
205 if exp_diff >= 128 {
206 small_frac = if small_frac != 0 { 1 } else { 0 };
207 } else {
208 let sticky = if small_frac & ((1u128 << exp_diff) - 1) != 0 {
209 1u128
210 } else {
211 0
212 };
213 small_frac = (small_frac >> exp_diff) | sticky;
214 }
215 }
216
217 let frac = big.frac.wrapping_sub(small_frac);
218 if frac == 0 {
219 let sign = env.round_mode() == RoundMode::Down;
220 let mut r = FloatParts {
221 sign,
222 exp: 0,
223 frac: 0,
224 cls: FloatClass::Zero,
225 };
226 return round_pack::<F>(&mut r, env);
227 }
228
229 let mut r = FloatParts {
230 sign,
231 exp: big.exp,
232 frac,
233 cls: FloatClass::Normal,
234 };
235 round_pack::<F>(&mut r, env)
236 }
237}
238
239fn mul_u128(a: u128, b: u128) -> (u128, u128) {
240 let a_lo = a as u64 as u128;
241 let a_hi = (a >> 64) as u64 as u128;
242 let b_lo = b as u64 as u128;
243 let b_hi = (b >> 64) as u64 as u128;
244
245 let ll = a_lo * b_lo;
246 let lh = a_lo * b_hi;
247 let hl = a_hi * b_lo;
248 let hh = a_hi * b_hi;
249
250 let mid = (ll >> 64)
251 + (lh & 0xFFFF_FFFF_FFFF_FFFF)
252 + (hl & 0xFFFF_FFFF_FFFF_FFFF);
253
254 let lo =
255 (ll & 0xFFFF_FFFF_FFFF_FFFF) | ((mid & 0xFFFF_FFFF_FFFF_FFFF) << 64);
256 let hi = hh + (lh >> 64) + (hl >> 64) + (mid >> 64);
257
258 (hi, lo)
259}
260
261macro_rules! impl_fma {
266 ($ty:ty) => {
267 impl $ty {
268 pub fn fma(self, b: Self, c: Self, env: &mut FloatEnv) -> Self {
269 fma::<Self>(self, b, c, env)
270 }
271 }
272 };
273}
274
275impl_fma!(Float16);
276impl_fma!(BFloat16);
277impl_fma!(Float32);
278impl_fma!(Float64);
279impl_fma!(Float128);
280impl_fma!(FloatX80);