1use super::int64::n64;
14use super::multiword::{Builder, Mw};
15
16pub const DEFAULT_NAN: u64 = 0xFFF8_0000_0000_0000;
18
19pub struct Unpacked {
21 pub bits: Mw,
22 pub sign: u32,
23 pub is_nan: u32,
24 pub is_inf: u32,
25 pub is_zero: u32,
26 pub exp: Mw,
28 pub sig: Mw,
30}
31
32fn c64(b: &mut Builder, v: u64) -> Mw {
33 let n = n64(b);
34 b.mw_const(v as u128, n)
35}
36
37pub fn unpack(b: &mut Builder, x: &Mw) -> Unpacked {
40 let n = x.len();
41 let sign = b.mw_top_bit(x);
42 let e_field = {
43 let s = b.mw_shr(x, 52);
44 let m = c64(b, 0x7FF);
45 b.mw_and(&s, &m)
46 };
47 let frac = {
48 let m = c64(b, (1u64 << 52) - 1);
49 b.mw_and(x, &m)
50 };
51 let zero = b.mw_zero(n);
52 let e_zero = b.mw_eq(&e_field, &zero);
53 let e_max_c = c64(b, 0x7FF);
54 let e_max = b.mw_eq(&e_field, &e_max_c);
55 let f_zero = b.mw_is_zero(&frac);
56 let f_nonzero = b.bit_not(f_zero);
57 let is_nan = b.and(e_max, f_nonzero);
58 let is_inf = b.and(e_max, f_zero);
59 let is_zero = b.and(e_zero, f_zero);
60 let hidden = c64(b, 1u64 << 52);
61 let with_hidden = b.mw_or(&frac, &hidden);
62 let raw_sig = b.mw_sel(e_zero, &frac, &with_hidden);
63 let one = c64(b, 1);
64 let raw_exp_biased = b.mw_sel(e_zero, &one, &e_field);
65 let bias = c64(b, 1023);
66 let raw_exp = b.mw_minus(&raw_exp_biased, &bias);
67 let nc = b.shape().words_for(16);
68 let lz = b.mw_clz(&raw_sig, nc);
69 let lz64 = b.mw_resize(&lz, n);
70 let eleven = c64(b, 11);
71 let norm_shift = b.mw_minus(&lz64, &eleven);
72 let norm_shift = b.mw_sel(is_zero, &zero, &norm_shift);
73 let sig = b.mw_shl_var(&raw_sig, &norm_shift, 7);
74 let exp = b.mw_minus(&raw_exp, &norm_shift);
75 Unpacked {
76 bits: x.clone(),
77 sign,
78 is_nan,
79 is_inf,
80 is_zero,
81 exp,
82 sig,
83 }
84}
85
86fn quiet(b: &mut Builder, x: &Mw) -> Mw {
88 let q = c64(b, 1u64 << 51);
89 b.mw_or(x, &q)
90}
91
92fn propagate(b: &mut Builder, x: &Unpacked, y: &Unpacked) -> Mw {
94 let qx = quiet(b, &x.bits);
95 let qy = quiet(b, &y.bits);
96 b.mw_sel(x.is_nan, &qx, &qy)
97}
98
99fn infinity(b: &mut Builder, sign: u32) -> Mw {
101 let pos = c64(b, 0x7FF0_0000_0000_0000);
102 let neg = c64(b, 0xFFF0_0000_0000_0000);
103 b.mw_sel(sign, &neg, &pos)
104}
105
106fn signed_zero(b: &mut Builder, sign: u32) -> Mw {
108 let pos = c64(b, 0);
109 let neg = c64(b, 1u64 << 63);
110 b.mw_sel(sign, &neg, &pos)
111}
112
113pub fn round_pack(b: &mut Builder, sign: u32, exp: &Mw, sig: &Mw) -> Mw {
117 let n = sig.len();
118 let bias = c64(b, 1023);
119 let be = b.mw_plus(exp, &bias);
120 let zero = b.mw_zero(n);
121 let one = c64(b, 1);
122 let be_pos = {
123 let le_zero = b.mw_lts(&be, &one);
124 b.bit_not(le_zero)
125 };
126 let under = b.mw_minus(&one, &be);
127 let cap = c64(b, 64);
128 let under_capped = {
129 let big = b.mw_lts(&cap, &under);
130 b.mw_sel(big, &cap, &under)
131 };
132 let shift = b.mw_sel(be_pos, &zero, &under_capped);
133 let (shifted, lost) = b.mw_shr_var_sticky(sig, &shift, 7);
134 let lost64 = b.mw_from_word(lost, n);
135 let jammed = b.mw_or(&shifted, &lost64);
136 let be_minus_1 = b.mw_minus(&be, &one);
137 let exp_field = b.mw_sel(be_pos, &be_minus_1, &zero);
138 let m = b.mw_shr(&jammed, 10);
139 let rmask = c64(b, 0x3FF);
140 let r = b.mw_and(&jammed, &rmask);
141 let half = c64(b, 0x200);
142 let above = b.mw_ltu(&half, &r);
143 let at_half = b.mw_eq(&r, &half);
144 let lsb = b.mw_bit(&m, 0);
145 let tie_up = b.and(at_half, lsb);
146 let inc = b.or(above, tie_up);
147 let (m_rounded, _) = b.mw_inc(&m, inc);
148 let placed_exp = b.mw_shl(&exp_field, 52);
149 let magnitude = b.mw_plus(&placed_exp, &m_rounded);
150 let sign_bit = {
151 let s = b.mw_from_word(sign, n);
152 b.mw_shl(&s, 63)
153 };
154 let packed = b.mw_or(&magnitude, &sign_bit);
155 let limit = c64(b, 0x7FF);
156 let overflow = {
157 let lt = b.mw_lts(&be, &limit);
158 b.bit_not(lt)
159 };
160 let inf = infinity(b, sign);
161 b.mw_sel(overflow, &inf, &packed)
162}
163
164fn normalize_to_62(b: &mut Builder, sig: &Mw) -> (Mw, Mw) {
167 let n = sig.len();
168 let nc = b.shape().words_for(16);
169 let lz = b.mw_clz(sig, nc);
170 let lz64 = b.mw_resize(&lz, n);
171 let one = c64(b, 1);
172 let top_set = b.mw_top_bit(sig);
173 let (down, lost) = b.mw_shr_sticky(sig, 1);
174 let lost64 = b.mw_from_word(lost, n);
175 let down = b.mw_or(&down, &lost64);
176 let left = b.mw_minus(&lz64, &one);
177 let up = b.mw_shl_var(sig, &left, 7);
178 let normalized = b.mw_sel(top_set, &down, &up);
179 let rise = b.mw_minus(&one, &lz64);
180 (normalized, rise)
181}
182
183pub fn add(b: &mut Builder, x: &Mw, y: &Mw, negate_b: bool) -> Mw {
185 let n = x.len();
186 let ux = unpack(b, x);
187 let uy = unpack(b, y);
188 let sy = if negate_b {
189 b.bit_not(uy.sign)
190 } else {
191 uy.sign
192 };
193 let zero = b.mw_zero(n);
194 let ex_gt = b.mw_lts(&uy.exp, &ux.exp);
196 let ex_eq = b.mw_eq(&ux.exp, &uy.exp);
197 let sig_ge = {
198 let lt = b.mw_ltu(&ux.sig, &uy.sig);
199 b.bit_not(lt)
200 };
201 let tie_ge = b.and(ex_eq, sig_ge);
202 let x_big = b.or(ex_gt, tie_ge);
203 let big_exp = b.mw_sel(x_big, &ux.exp, &uy.exp);
204 let small_exp = b.mw_sel(x_big, &uy.exp, &ux.exp);
205 let big_sig = b.mw_sel(x_big, &ux.sig, &uy.sig);
206 let small_sig = b.mw_sel(x_big, &uy.sig, &ux.sig);
207 let big_sign = b.sel(x_big, ux.sign, sy);
208 let small_sign = b.sel(x_big, sy, ux.sign);
209 let d = b.mw_minus(&big_exp, &small_exp);
210 let cap = c64(b, 64);
211 let d_capped = {
212 let big = b.mw_lts(&cap, &d);
213 b.mw_sel(big, &cap, &d)
214 };
215 let big9 = b.mw_shl(&big_sig, 9);
216 let small9 = b.mw_shl(&small_sig, 9);
217 let (aligned, lost) = b.mw_shr_var_sticky(&small9, &d_capped, 7);
218 let lost64 = b.mw_from_word(lost, n);
219 let aligned = b.mw_or(&aligned, &lost64);
220 let same_sign = {
221 let differ = b.xor(big_sign, small_sign);
222 b.bit_not(differ)
223 };
224 let sum = b.mw_plus(&big9, &aligned);
225 let diff = b.mw_minus(&big9, &aligned);
226 let raw = b.mw_sel(same_sign, &sum, &diff);
227 let raw_zero = b.mw_is_zero(&raw);
228 let (norm, rise) = normalize_to_62(b, &raw);
229 let one = c64(b, 1);
231 let e1 = b.mw_plus(&big_exp, &rise);
232 let e = b.mw_plus(&e1, &one);
233 let finite = round_pack(b, big_sign, &e, &norm);
234 let pos_zero = c64(b, 0);
236 let y_signed = {
238 let flip = if negate_b {
239 c64(b, 1u64 << 63)
240 } else {
241 b.mw_zero(n)
242 };
243 b.mw_xor(y, &flip)
244 };
245 let both_zero = b.and(ux.is_zero, uy.is_zero);
246 let zeros_sign = b.and(ux.sign, sy);
247 let zz = signed_zero(b, zeros_sign);
248 let mut out = b.mw_sel(raw_zero, &pos_zero, &finite);
249 out = b.mw_sel(uy.is_zero, x, &out);
250 out = b.mw_sel(ux.is_zero, &y_signed, &out);
251 out = b.mw_sel(both_zero, &zz, &out);
252 let both_inf = b.and(ux.is_inf, uy.is_inf);
254 let opposite = b.xor(ux.sign, sy);
255 let invalid = b.and(both_inf, opposite);
256 let inf_x = infinity(b, ux.sign);
257 let inf_y = infinity(b, sy);
258 out = b.mw_sel(uy.is_inf, &inf_y, &out);
259 out = b.mw_sel(ux.is_inf, &inf_x, &out);
260 let dnan = c64(b, DEFAULT_NAN);
261 out = b.mw_sel(invalid, &dnan, &out);
262 let any_nan = b.or(ux.is_nan, uy.is_nan);
263 let nan = propagate(b, &ux, &uy);
264 let _ = zero;
265 b.mw_sel(any_nan, &nan, &out)
266}
267
268pub fn mul(b: &mut Builder, x: &Mw, y: &Mw) -> Mw {
270 let n = x.len();
271 let ux = unpack(b, x);
272 let uy = unpack(b, y);
273 let sign = b.xor(ux.sign, uy.sign);
274 let p = b.mw_mul(&ux.sig, &uy.sig);
275 let top = b.mw_bit(&p, 105);
276 let (s43, l43) = b.mw_shr_sticky(&p, 43);
277 let (s42, l42) = b.mw_shr_sticky(&p, 42);
278 let s43 = s43.slice(0, n);
279 let s42 = s42.slice(0, n);
280 let lost = b.sel(top, l43, l42);
281 let chosen = b.mw_sel(top, &s43, &s42);
282 let lost64 = b.mw_from_word(lost, n);
283 let sig = b.mw_or(&chosen, &lost64);
284 let e = b.mw_plus(&ux.exp, &uy.exp);
285 let top64 = b.mw_from_word(top, n);
286 let e = b.mw_plus(&e, &top64);
287 let finite = round_pack(b, sign, &e, &sig);
288 let zero = signed_zero(b, sign);
289 let inf = infinity(b, sign);
290 let any_zero = b.or(ux.is_zero, uy.is_zero);
291 let any_inf = b.or(ux.is_inf, uy.is_inf);
292 let invalid = b.and(any_zero, any_inf);
293 let mut out = b.mw_sel(any_zero, &zero, &finite);
294 out = b.mw_sel(any_inf, &inf, &out);
295 let dnan = c64(b, DEFAULT_NAN);
296 out = b.mw_sel(invalid, &dnan, &out);
297 let any_nan = b.or(ux.is_nan, uy.is_nan);
298 let nan = propagate(b, &ux, &uy);
299 b.mw_sel(any_nan, &nan, &out)
300}
301
302pub fn div(b: &mut Builder, x: &Mw, y: &Mw) -> Mw {
305 let n = x.len();
306 let ux = unpack(b, x);
307 let uy = unpack(b, y);
308 let sign = b.xor(ux.sign, uy.sign);
309 let wide = 2 * n;
311 let num = b.mw_resize(&ux.sig, wide);
312 let num = b.mw_shl(&num, 62);
313 let one_w = b.mw_const(1, wide);
314 let den = b.mw_resize(&uy.sig, wide);
315 let den_zero = b.mw_is_zero(&den);
316 let den = b.mw_sel(den_zero, &one_w, &den);
317 let (q, r) = b.mw_divrem(&num, &den);
318 let r_nonzero = {
319 let z = b.mw_is_zero(&r);
320 b.bit_not(z)
321 };
322 let q = q.slice(0, n);
323 let sticky = b.mw_from_word(r_nonzero, n);
324 let q = b.mw_or(&q, &sticky);
325 let (norm, rise) = normalize_to_62(b, &q);
326 let e = b.mw_minus(&ux.exp, &uy.exp);
327 let e = b.mw_plus(&e, &rise);
328 let finite = round_pack(b, sign, &e, &norm);
329 let zero = signed_zero(b, sign);
330 let inf = infinity(b, sign);
331 let both_zero = b.and(ux.is_zero, uy.is_zero);
332 let both_inf = b.and(ux.is_inf, uy.is_inf);
333 let invalid = b.or(both_zero, both_inf);
334 let mut out = b.mw_sel(uy.is_inf, &zero, &finite);
335 out = b.mw_sel(ux.is_zero, &zero, &out);
336 out = b.mw_sel(uy.is_zero, &inf, &out);
337 out = b.mw_sel(ux.is_inf, &inf, &out);
338 let dnan = c64(b, DEFAULT_NAN);
339 out = b.mw_sel(invalid, &dnan, &out);
340 let any_nan = b.or(ux.is_nan, uy.is_nan);
341 let nan = propagate(b, &ux, &uy);
342 b.mw_sel(any_nan, &nan, &out)
343}
344
345pub fn sqrt(b: &mut Builder, x: &Mw) -> Mw {
347 let n = x.len();
348 let ux = unpack(b, x);
349 let odd = b.mw_bit(&ux.exp, 0);
351 let one = c64(b, 1);
352 let odd64 = b.mw_from_word(odd, n);
353 let e_even = b.mw_minus(&ux.exp, &odd64);
354 let sig2 = b.mw_shl(&ux.sig, 1);
355 let m = b.mw_sel(odd, &sig2, &ux.sig);
356 let wide = 2 * n;
358 let rad = b.mw_resize(&m, wide);
359 let rad = b.mw_shl(&rad, 72);
360 let rad_state = b.mw_state(&rad);
361 let zero_w = b.mw_zero(wide);
362 let rem = b.mw_state(&zero_w);
363 let root = b.mw_state(&zero_w);
364 b.repeat(64, |s| {
365 let rem4 = s.mw_shl(&rem, 2);
366 let top2 = s.mw_shr(&rad_state, 126);
367 let rem_in = s.mw_or(&rem4, &top2);
368 let rad2 = s.mw_shl(&rad_state, 2);
369 let root4 = s.mw_shl(&root, 2);
370 let one_w = s.mw_const(1, wide);
371 let trial = s.mw_or(&root4, &one_w);
372 let z = s.k(0);
373 let (d, borrow) = s.mw_sub(&rem_in, &trial, z);
374 let fits = s.bit_not(borrow);
375 let rem2 = s.mw_sel(fits, &d, &rem_in);
376 let root2 = s.mw_shl(&root, 1);
377 let fits_w = s.mw_from_word(fits, wide);
378 let root3 = s.mw_or(&root2, &fits_w);
379 s.mw_assign(&rem, &rem2);
380 s.mw_assign(&root, &root3);
381 s.mw_assign(&rad_state, &rad2);
382 });
383 let r_nonzero = {
384 let z = b.mw_is_zero(&rem);
385 b.bit_not(z)
386 };
387 let sig = root.slice(0, n);
388 let sticky = b.mw_from_word(r_nonzero, n);
389 let sig = b.mw_or(&sig, &sticky);
390 let e = b.mw_sar(&e_even, 1);
391 let positive = b.k(0);
392 let finite = round_pack(b, positive, &e, &sig);
393 let neg_nonzero = {
394 let nz = b.bit_not(ux.is_zero);
395 b.and(ux.sign, nz)
396 };
397 let pos_inf = c64(b, f64::INFINITY.to_bits());
398 let mut out = b.mw_sel(ux.is_inf, &pos_inf, &finite);
399 out = b.mw_sel(ux.is_zero, x, &out);
400 let dnan = c64(b, DEFAULT_NAN);
401 out = b.mw_sel(neg_nonzero, &dnan, &out);
402 let qx = quiet(b, x);
403 let _ = one;
404 b.mw_sel(ux.is_nan, &qx, &out)
405}
406
407pub fn compare(b: &mut Builder, x: &Mw, y: &Mw) -> (u32, u32, u32) {
409 let ux = unpack(b, x);
410 let uy = unpack(b, y);
411 let any_nan = b.or(ux.is_nan, uy.is_nan);
412 let ordered = b.bit_not(any_nan);
413 let both_zero = b.and(ux.is_zero, uy.is_zero);
414 let same_bits = b.mw_eq(x, y);
415 let eq_raw = b.or(same_bits, both_zero);
416 let eq = b.and(ordered, eq_raw);
417 let differ = b.xor(ux.sign, uy.sign);
418 let mag_x = {
419 let m = c64(b, u64::MAX >> 1);
420 b.mw_and(x, &m)
421 };
422 let mag_y = {
423 let m = c64(b, u64::MAX >> 1);
424 b.mw_and(y, &m)
425 };
426 let x_lt_mag = b.mw_ltu(&mag_x, &mag_y);
427 let y_lt_mag = b.mw_ltu(&mag_y, &mag_x);
428 let same_sign_lt = b.sel(ux.sign, y_lt_mag, x_lt_mag);
429 let lt_raw = b.sel(differ, ux.sign, same_sign_lt);
430 let not_zeros = b.bit_not(both_zero);
431 let lt1 = b.and(lt_raw, not_zeros);
432 let lt = b.and(lt1, ordered);
433 let le = b.or(lt, eq);
434 (lt, le, eq)
435}
436
437pub fn from_int(b: &mut Builder, a: &Mw) -> Mw {
439 let n = a.len();
440 let sign = b.mw_top_bit(a);
441 let neg = b.mw_neg(a);
442 let mag = b.mw_sel(sign, &neg, a);
443 let is_zero = b.mw_is_zero(a);
444 let (norm, rise) = normalize_to_62(b, &mag);
445 let e62 = c64(b, 62);
446 let e = b.mw_plus(&e62, &rise);
447 let finite = round_pack(b, sign, &e, &norm);
448 let z = b.mw_zero(n);
449 b.mw_sel(is_zero, &z, &finite)
450}
451
452pub fn floor_to_int(b: &mut Builder, x: &Mw) -> Mw {
454 let n = x.len();
455 let ux = unpack(b, x);
456 let zero = b.mw_zero(n);
457 let minus_one = c64(b, u64::MAX);
458 let i_max = c64(b, i64::MAX as u64);
459 let i_min = c64(b, i64::MIN as u64);
460 let e_neg = b.mw_top_bit(&ux.exp);
462 let small = b.mw_sel(ux.sign, &minus_one, &zero);
463 let c52 = c64(b, 52);
465 let down_amt = b.mw_minus(&c52, &ux.exp);
466 let up_amt = b.mw_minus(&ux.exp, &c52);
467 let e_le_52 = {
468 let gt = b.mw_lts(&c52, &ux.exp);
469 b.bit_not(gt)
470 };
471 let (down, frac) = b.mw_shr_var_sticky(&ux.sig, &down_amt, 6);
472 let up = b.mw_shl_var(&ux.sig, &up_amt, 4);
473 let int_part = b.mw_sel(e_le_52, &down, &up);
474 let had_frac = b.and(e_le_52, frac);
475 let neg_int = b.mw_neg(&int_part);
476 let (neg_floor, _) = {
477 let z = b.k(0);
478 let dec = b.mw_from_word(had_frac, n);
479 b.mw_sub(&neg_int, &dec, z)
480 };
481 let mid = b.mw_sel(ux.sign, &neg_floor, &int_part);
482 let c63 = c64(b, 63);
484 let huge = {
485 let lt = b.mw_lts(&ux.exp, &c63);
486 b.bit_not(lt)
487 };
488 let sat = b.mw_sel(ux.sign, &i_min, &i_max);
489 let mut out = b.mw_sel(e_neg, &small, &mid);
490 out = b.mw_sel(huge, &sat, &out);
491 out = b.mw_sel(ux.is_inf, &sat, &out);
492 out = b.mw_sel(ux.is_zero, &zero, &out);
493 b.mw_sel(ux.is_nan, &zero, &out)
494}
495
496pub fn abs(b: &mut Builder, x: &Mw) -> Mw {
498 let m = c64(b, u64::MAX >> 1);
499 b.mw_and(x, &m)
500}
501
502fn max_ordered(b: &mut Builder, x: &Mw, y: &Mw) -> Mw {
504 let (lt, _, _) = compare(b, x, y);
505 b.mw_sel(lt, y, x)
506}
507
508pub fn approx_eq_ints(b: &mut Builder, x: &Mw, y: &Mw) -> u32 {
511 let fx = from_int(b, x);
512 let fy = from_int(b, y);
513 let (_, _, eq) = compare(b, &fx, &fy);
514 let d = add(b, &fx, &fy, true);
515 let diff = abs(b, &d);
516 let ax = abs(b, &fx);
517 let ay = abs(b, &fy);
518 let m = max_ordered(b, &ax, &ay);
519 let tiny = c64(b, 1e-9f64.to_bits());
520 let scaled = mul(b, &tiny, &m);
521 let floor = c64(b, 1e-12f64.to_bits());
522 let bound = max_ordered(b, &scaled, &floor);
523 let (_, within, _) = compare(b, &diff, &bound);
524 b.or(eq, within)
525}