1use core::{f32, f64};
2
3use super::scalbn;
4
5const ZEROINFNAN: i32 = 0x7ff - 0x3ff - 52 - 1;
6
7struct Num {
8 m: u64,
9 e: i32,
10 sign: i32,
11}
12
13fn normalize(x: f64) -> Num {
14 let x1p63: f64 = f64::from_bits(0x43e0000000000000); let mut ix: u64 = x.to_bits();
17 let mut e: i32 = (ix >> 52) as i32;
18 let sign: i32 = e & 0x800;
19 e &= 0x7ff;
20 if e == 0 {
21 ix = (x * x1p63).to_bits();
22 e = (ix >> 52) as i32 & 0x7ff;
23 e = if e != 0 { e - 63 } else { 0x800 };
24 }
25 ix &= (1 << 52) - 1;
26 ix |= 1 << 52;
27 ix <<= 1;
28 e -= 0x3ff + 52 + 1;
29 Num { m: ix, e, sign }
30}
31
32fn mul(x: u64, y: u64) -> (u64, u64) {
33 let t1: u64;
34 let t2: u64;
35 let t3: u64;
36 let xlo: u64 = x as u32 as u64;
37 let xhi: u64 = x >> 32;
38 let ylo: u64 = y as u32 as u64;
39 let yhi: u64 = y >> 32;
40
41 t1 = xlo * ylo;
42 t2 = xlo * yhi + xhi * ylo;
43 t3 = xhi * yhi;
44 let lo = t1.wrapping_add(t2 << 32);
45 let hi = t3 + (t2 >> 32) + (t1 > lo) as u64;
46 (hi, lo)
47}
48
49#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)]
55pub fn fma(x: f64, y: f64, z: f64) -> f64 {
56 let x1p63: f64 = f64::from_bits(0x43e0000000000000); let x0_ffffff8p_63 = f64::from_bits(0x3bfffffff0000000); let nx = normalize(x);
61 let ny = normalize(y);
62 let nz = normalize(z);
63
64 if nx.e >= ZEROINFNAN || ny.e >= ZEROINFNAN {
65 return x * y + z;
66 }
67 if nz.e >= ZEROINFNAN {
68 if nz.e > ZEROINFNAN {
69 return x * y + z;
71 }
72 return z;
73 }
74
75 let zhi: u64;
77 let zlo: u64;
78 let (mut rhi, mut rlo) = mul(nx.m, ny.m);
79 let mut e: i32 = nx.e + ny.e;
83 let mut d: i32 = nz.e - e;
84 if d > 0 {
86 if d < 64 {
87 zlo = nz.m << d;
88 zhi = nz.m >> (64 - d);
89 } else {
90 zlo = 0;
91 zhi = nz.m;
92 e = nz.e - 64;
93 d -= 64;
94 if d == 0 {
95 } else if d < 64 {
96 rlo = rhi << (64 - d) | rlo >> d | ((rlo << (64 - d)) != 0) as u64;
97 rhi = rhi >> d;
98 } else {
99 rlo = 1;
100 rhi = 0;
101 }
102 }
103 } else {
104 zhi = 0;
105 d = -d;
106 if d == 0 {
107 zlo = nz.m;
108 } else if d < 64 {
109 zlo = nz.m >> d | ((nz.m << (64 - d)) != 0) as u64;
110 } else {
111 zlo = 1;
112 }
113 }
114
115 let mut sign: i32 = nx.sign ^ ny.sign;
117 let samesign: bool = (sign ^ nz.sign) == 0;
118 let mut nonzero: i32 = 1;
119 if samesign {
120 rlo = rlo.wrapping_add(zlo);
122 rhi += zhi + (rlo < zlo) as u64;
123 } else {
124 let (res, borrow) = rlo.overflowing_sub(zlo);
126 rlo = res;
127 rhi = rhi.wrapping_sub(zhi.wrapping_add(borrow as u64));
128 if (rhi >> 63) != 0 {
129 rlo = (-(rlo as i64)) as u64;
130 rhi = (-(rhi as i64)) as u64 - (rlo != 0) as u64;
131 sign = (sign == 0) as i32;
132 }
133 nonzero = (rhi != 0) as i32;
134 }
135
136 if nonzero != 0 {
138 e += 64;
139 d = rhi.leading_zeros() as i32 - 1;
140 rhi = rhi << d | rlo >> (64 - d) | ((rlo << d) != 0) as u64;
142 } else if rlo != 0 {
143 d = rlo.leading_zeros() as i32 - 1;
144 if d < 0 {
145 rhi = rlo >> 1 | (rlo & 1);
146 } else {
147 rhi = rlo << d;
148 }
149 } else {
150 return x * y + z;
152 }
153 e -= d;
154
155 let mut i: i64 = rhi as i64; if sign != 0 {
158 i = -i;
159 }
160 let mut r: f64 = i as f64; if e < -1022 - 62 {
163 if e == -1022 - 63 {
165 let mut c: f64 = x1p63;
166 if sign != 0 {
167 c = -c;
168 }
169 if r == c {
170 let fltmin: f32 = (x0_ffffff8p_63 * f32::MIN_POSITIVE as f64 * r) as f32;
174 return f64::MIN_POSITIVE / f32::MIN_POSITIVE as f64 * fltmin as f64;
175 }
176 if (rhi << 53) != 0 {
179 i = (rhi >> 1 | (rhi & 1) | 1 << 62) as i64;
180 if sign != 0 {
181 i = -i;
182 }
183 r = i as f64;
184 r = 2. * r - c; {
189 let tiny: f64 = f64::MIN_POSITIVE / f32::MIN_POSITIVE as f64 * r;
190 r += (tiny * tiny) * (r - r);
191 }
192 }
193 } else {
194 d = 10;
196 i = ((rhi >> d | ((rhi << (64 - d)) != 0) as u64) << d) as i64;
197 if sign != 0 {
198 i = -i;
199 }
200 r = i as f64;
201 }
202 }
203 scalbn(r, e)
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 #[test]
210 fn fma_segfault() {
211 assert_eq!(
213 fma(
214 -0.0000000000000002220446049250313,
215 -0.0000000000000002220446049250313,
216 -0.0000000000000002220446049250313
217 ),
218 -0.00000000000000022204460492503126,
219 );
220
221 let result = fma(-0.992, -0.992, -0.992);
222 #[cfg(all(target_arch = "x86", not(target_feature = "sse2")))]
224 let result = force_eval!(result);
225 assert_eq!(result, -0.007936000000000007,);
226 }
227
228 #[test]
229 fn fma_sbb() {
230 assert_eq!(
231 fma(-(1.0 - f64::EPSILON), f64::MIN, f64::MIN),
232 -3991680619069439e277
233 );
234 }
235}