1use binius_core::Word;
3use binius_frontend::{CircuitBuilder, Wire};
4
5#[derive(Clone, Copy)]
7pub struct Fp64Parts {
8 pub sign: Wire, pub exp: Wire, pub frac: Wire, pub is_nan: Wire, pub is_inf: Wire, pub is_zero: Wire, pub is_sub: Wire, pub is_norm: Wire, }
19
20pub fn zero(b: &CircuitBuilder) -> Wire {
22 b.add_constant_64(0)
23}
24
25pub fn one(b: &CircuitBuilder) -> Wire {
27 b.add_constant_64(1)
28}
29
30#[inline]
34pub fn bit_msb01(b: &CircuitBuilder, x: Wire, i: u32) -> Wire {
35 b.shl(bit_lsb(b, x, i), 63)
36}
37
38#[inline]
40pub fn msb_to_lsb01(b: &CircuitBuilder, b_msb: Wire) -> Wire {
41 b.shr(b_msb, 63)
42}
43
44pub fn isub(builder: &CircuitBuilder, a: Wire, b: Wire) -> Wire {
49 let (d, _b) = builder.isub_bin_bout(a, b, zero(builder));
50 d
51}
52
53pub fn bit_lsb(b: &CircuitBuilder, x: Wire, i: u32) -> Wire {
57 b.band(b.shr(x, i), one(b))
58}
59
60pub fn var_shr_with_sticky(
75 b: &CircuitBuilder,
76 x: Wire,
77 d: Wire,
78 saturate_at_63: bool,
79) -> (Wire, Wire) {
80 let c63 = b.add_constant_64(63);
81 let c64 = b.add_constant_64(64);
82 let d_eff = if saturate_at_63 {
83 let lt64 = b.icmp_ult(d, c64);
84 b.select(lt64, d, c63)
85 } else {
86 b.band(d, c63)
87 };
88
89 let mut v = x;
90 let mut sticky01 = zero(b); {
94 let cond01 = bit_lsb(b, d_eff, 5);
95 let cond = bit_msb01(b, d_eff, 5);
96 let lost = b.band(v, b.add_constant_64((1u64 << 32) - 1));
97 let lost_ne0 = b.icmp_ne(lost, zero(b));
98 let lost_nz01 = msb_to_lsb01(b, lost_ne0);
99 sticky01 = b.bor(sticky01, b.band(lost_nz01, cond01));
100 let shifted = b.shr(v, 32);
101 v = b.select(cond, shifted, v);
102 }
103 {
105 let cond01 = bit_lsb(b, d_eff, 4);
106 let cond = bit_msb01(b, d_eff, 4);
107 let lost = b.band(v, b.add_constant_64((1u64 << 16) - 1));
108 let lost_ne0 = b.icmp_ne(lost, zero(b));
109 let lost_nz01 = msb_to_lsb01(b, lost_ne0);
110 sticky01 = b.bor(sticky01, b.band(lost_nz01, cond01));
111 let shifted = b.shr(v, 16);
112 v = b.select(cond, shifted, v);
113 }
114 {
116 let cond01 = bit_lsb(b, d_eff, 3);
117 let cond = bit_msb01(b, d_eff, 3);
118 let lost = b.band(v, b.add_constant_64((1u64 << 8) - 1));
119 let lost_ne0 = b.icmp_ne(lost, zero(b));
120 let lost_nz01 = msb_to_lsb01(b, lost_ne0);
121 sticky01 = b.bor(sticky01, b.band(lost_nz01, cond01));
122 let shifted = b.shr(v, 8);
123 v = b.select(cond, shifted, v);
124 }
125 {
127 let cond01 = bit_lsb(b, d_eff, 2);
128 let cond = bit_msb01(b, d_eff, 2);
129 let lost = b.band(v, b.add_constant_64((1u64 << 4) - 1));
130 let lost_ne0 = b.icmp_ne(lost, zero(b));
131 let lost_nz01 = msb_to_lsb01(b, lost_ne0);
132 sticky01 = b.bor(sticky01, b.band(lost_nz01, cond01));
133 let shifted = b.shr(v, 4);
134 v = b.select(cond, shifted, v);
135 }
136 {
138 let cond01 = bit_lsb(b, d_eff, 1);
139 let cond = bit_msb01(b, d_eff, 1);
140 let lost = b.band(v, b.add_constant_64((1u64 << 2) - 1));
141 let lost_ne0 = b.icmp_ne(lost, zero(b));
142 let lost_nz01 = msb_to_lsb01(b, lost_ne0);
143 sticky01 = b.bor(sticky01, b.band(lost_nz01, cond01));
144 let shifted = b.shr(v, 2);
145 v = b.select(cond, shifted, v);
146 }
147 {
149 let cond01 = bit_lsb(b, d_eff, 0);
150 let cond = bit_msb01(b, d_eff, 0);
151 let lost = b.band(v, b.add_constant_64(1));
152 let lost_ne0 = b.icmp_ne(lost, zero(b));
153 let lost_nz01 = msb_to_lsb01(b, lost_ne0);
154 sticky01 = b.bor(sticky01, b.band(lost_nz01, cond01));
155 let shifted = b.shr(v, 1);
156 v = b.select(cond, shifted, v);
157 }
158
159 (v, sticky01)
160}
161
162pub fn var_shl(b: &CircuitBuilder, x: Wire, d: Wire) -> Wire {
174 let d_eff = b.band(d, b.add_constant_64(63));
175 let mut v = x;
176
177 {
178 let cond = bit_msb01(b, d_eff, 5); let shifted = b.shl(v, 32);
180 v = b.select(cond, shifted, v);
181 }
182 {
183 let cond = bit_msb01(b, d_eff, 4); let shifted = b.shl(v, 16);
185 v = b.select(cond, shifted, v);
186 }
187 {
188 let cond = bit_msb01(b, d_eff, 3); let shifted = b.shl(v, 8);
190 v = b.select(cond, shifted, v);
191 }
192 {
193 let cond = bit_msb01(b, d_eff, 2); let shifted = b.shl(v, 4);
195 v = b.select(cond, shifted, v);
196 }
197 {
198 let cond = bit_msb01(b, d_eff, 1); let shifted = b.shl(v, 2);
200 v = b.select(cond, shifted, v);
201 }
202 {
203 let cond = bit_msb01(b, d_eff, 0); let shifted = b.shl(v, 1);
205 v = b.select(cond, shifted, v);
206 }
207
208 v
209}
210
211pub fn clz64(b: &CircuitBuilder, x: Wire) -> Wire {
213 let mut n = zero(b);
214 let mut y = x;
215
216 {
218 let t = b.shr(y, 32);
219 let z = b.icmp_eq(t, zero(b));
220 let add32 = b.add_constant_64(32);
221 n = b.iadd(n, b.select(z, add32, zero(b))).0;
222 y = b.select(z, b.shl(y, 32), y);
223 }
224 {
226 let t = b.shr(y, 48);
227 let z = b.icmp_eq(t, zero(b));
228 let add16 = b.add_constant_64(16);
229 n = b.iadd(n, b.select(z, add16, zero(b))).0;
230 y = b.select(z, b.shl(y, 16), y);
231 }
232 {
234 let t = b.shr(y, 56);
235 let z = b.icmp_eq(t, zero(b));
236 let add8 = b.add_constant_64(8);
237 n = b.iadd(n, b.select(z, add8, zero(b))).0;
238 y = b.select(z, b.shl(y, 8), y);
239 }
240 {
242 let t = b.shr(y, 60);
243 let z = b.icmp_eq(t, zero(b));
244 let add4 = b.add_constant_64(4);
245 n = b.iadd(n, b.select(z, add4, zero(b))).0;
246 y = b.select(z, b.shl(y, 4), y);
247 }
248 {
250 let t = b.shr(y, 62);
251 let z = b.icmp_eq(t, zero(b));
252 let add2 = b.add_constant_64(2);
253 n = b.iadd(n, b.select(z, add2, zero(b))).0;
254 y = b.select(z, b.shl(y, 2), y);
255 }
256 {
258 let t = b.shr(y, 63);
259 let z = b.icmp_eq(t, zero(b));
260 n = b.iadd(n, b.select(z, one(b), zero(b))).0;
261 }
262 n
263}
264
265pub fn fp64_sig53_and_exp(b: &CircuitBuilder, p: &Fp64Parts) -> (Wire, Wire) {
279 let one52 = b.add_constant_64(1u64 << 52);
280 let sig_norm = b.bor(one52, p.frac);
281 let sig = b.select(p.is_norm, sig_norm, p.frac);
282 let exp_eff = b.select(p.is_norm, p.exp, one(b)); (sig, exp_eff)
284}
285
286#[inline]
298pub fn shr128_to_u64_const(b: &CircuitBuilder, hi: Wire, lo: Wire, s: u32) -> Wire {
299 debug_assert!(s > 0 && s < 64);
300 let lo_part = b.shr(lo, s);
301 let hi_part = b.shl(hi, 64 - s);
302 b.bor(lo_part, hi_part)
303}
304
305#[inline]
317pub fn sticky_from_low_k(b: &CircuitBuilder, lo: Wire, k: u32) -> Wire {
318 debug_assert!(k < 64);
319 let mask = b.add_constant_64((1u64 << k) - 1);
320 let ne0 = b.icmp_ne(b.band(lo, mask), zero(b));
321 msb_to_lsb01(b, ne0)
322}
323
324pub fn fp64_unpack(b: &CircuitBuilder, x: Wire) -> Fp64Parts {
342 let exp_m = b.add_constant_64(0x7FF);
343 let frac_m = b.add_constant_64((1u64 << 52) - 1);
344
345 let sign = x;
346 let exp = b.band(b.shr(x, 52), exp_m);
347 let frac = b.band(x, frac_m);
348
349 let exp_is_max = b.icmp_eq(exp, exp_m); let exp_is_zero = b.icmp_eq(exp, zero(b)); let frac_is_zero = b.icmp_eq(frac, zero(b)); let is_nan = b.band(exp_is_max, b.bnot(frac_is_zero));
354 let is_inf = b.band(exp_is_max, frac_is_zero);
355 let is_zero = b.band(exp_is_zero, frac_is_zero);
356 let is_sub = b.band(exp_is_zero, b.bnot(frac_is_zero));
357 let is_norm = b.bnot(b.bor(exp_is_max, exp_is_zero));
358
359 Fp64Parts {
360 sign,
361 exp,
362 frac,
363 is_nan,
364 is_inf,
365 is_zero,
366 is_sub,
367 is_norm,
368 }
369}
370
371pub fn fp64_underflow_shift(
379 b: &CircuitBuilder,
380 res_sig: Wire,
381 res_exp: Wire,
382) -> (Wire, Wire, Wire) {
383 let c1 = one(b);
384
385 let exp_lt_1 = b.icmp_ult(res_exp, c1);
386 let k = isub(b, c1, res_exp); let k_use = b.select(exp_lt_1, k, zero(b));
388
389 let (mut sig_u, st) = var_shr_with_sticky(b, res_sig, k_use, true);
390 sig_u = b.bor(sig_u, b.band(st, c1));
393
394 let sig_round_base = b.select(exp_lt_1, sig_u, res_sig);
395 let exp_round_base = b.select(exp_lt_1, c1, res_exp);
396 (sig_round_base, exp_round_base, exp_lt_1)
397}
398
399pub fn fp64_round_rne(b: &CircuitBuilder, sig_base: Wire, exp_base: Wire) -> (Wire, Wire, Wire) {
413 let lsb = bit_lsb(b, sig_base, 11);
414 let g: Wire = bit_lsb(b, sig_base, 10);
415 let r = bit_lsb(b, sig_base, 9);
416 let s = b.band(sig_base, one(b));
417 let r_or_s = b.bor(r, s);
418 let tie_or_gt = b.bor(r_or_s, lsb);
419 let round_up01 = b.band(g, tie_or_gt); let mant_trunc = b.shr(sig_base, 11);
422 let mant_rounded = b.iadd(mant_trunc, round_up01).0;
423
424 let overflow01 = bit_lsb(b, mant_rounded, 53); let overflow_msb = bit_msb01(b, mant_rounded, 53);
426 let mant_final_53 = b.select(overflow_msb, b.shr(mant_rounded, 1), mant_rounded);
427 let exp_after = b.iadd(exp_base, overflow01).0;
428
429 (mant_final_53, exp_after, overflow_msb)
430}
431
432pub fn fp64_pack_finite_or_inf(
441 b: &CircuitBuilder,
442 sign: Wire,
443 mant_final_53: Wire,
444 exp_after_round: Wire,
445 stayed_sub_mask: Wire,
446) -> Wire {
447 let frac_m = b.add_constant_64((1u64 << 52) - 1);
448 let exp_2047 = b.add_constant_64(0x7FF);
449
450 let frac = b.band(mant_final_53, frac_m);
451 let sign = b.band(sign, b.add_constant(Word::MSB_ONE));
452 let packed_sub = b.bor(sign, frac);
453
454 let exp_sh = b.shl(exp_after_round, 52);
455 let packed_norm = b.bor(b.bor(sign, exp_sh), frac);
456
457 let finite_packed = b.select(stayed_sub_mask, packed_sub, packed_norm);
458
459 let mant_is_zero = b.icmp_eq(mant_final_53, zero(b));
461 let finite_or_zero = b.select(mant_is_zero, sign, finite_packed);
462
463 let overflow_to_inf = b.icmp_ule(exp_2047, exp_after_round);
465 let inf_payload = b.shl(exp_2047, 52);
466 let packed_inf = b.bor(sign, inf_payload);
467
468 b.select(overflow_to_inf, packed_inf, finite_or_zero)
469}
470
471#[cfg(test)]
472pub mod tests {
473 use binius_core::word::Word;
474 use proptest::bool;
475
476 use super::*;
477
478 pub struct Fp64UnpackResult {
480 pub sign: u64,
481 pub exp: u64,
482 pub frac: u64,
483 pub is_nan: u64, pub is_inf: u64, pub is_norm: u64, }
487
488 pub fn ref_fp64_unpack(x: u64) -> Fp64UnpackResult {
489 let sign = x & (1u64 << 63);
490 let exp = (x >> 52) & 0x7FF;
491 let frac = x & ((1u64 << 52) - 1);
492
493 let exp_is_max = if exp == 0x7FF { 1u64 << 63 } else { 0 };
494 let exp_is_zero = if exp == 0 { 1u64 << 63 } else { 0 };
495 let frac_is_zero = if frac == 0 { 1u64 << 63 } else { 0 };
496
497 let is_nan = if exp_is_max != 0 && frac_is_zero == 0 {
499 1u64 << 63
500 } else {
501 0
502 };
503 let is_inf = if exp_is_max != 0 && frac_is_zero != 0 {
504 1u64 << 63
505 } else {
506 0
507 };
508 let is_norm = if exp_is_max == 0 && exp_is_zero == 0 {
509 1u64 << 63
510 } else {
511 0
512 };
513
514 Fp64UnpackResult {
515 sign,
516 exp,
517 frac,
518 is_nan,
519 is_inf,
520 is_norm,
521 }
522 }
523
524 pub fn ref_fp64_underflow_shift(res_sig: u64, res_exp: u64) -> (u64, u64, u64) {
525 let exp_lt_1 = if res_exp < 1 { 1u64 << 63 } else { 0 };
526
527 if exp_lt_1 != 0 {
528 let k = 1u64.wrapping_sub(res_exp);
529 let k_use = std::cmp::min(k, 63);
530
531 let sig_shifted = res_sig >> k_use;
532 let lost_bits = res_sig & ((1u64 << k_use) - 1);
533 let sticky = if lost_bits != 0 { 1 } else { 0 };
534 let bit0 = (sig_shifted & 1) | sticky;
535 let sig_u = (sig_shifted & !1) | bit0;
536
537 (sig_u, 1, exp_lt_1)
538 } else {
539 (res_sig, res_exp, exp_lt_1)
540 }
541 }
542
543 pub fn ref_fp64_round_rne(sig_base: u64, exp_base: u64) -> (u64, u64, u64) {
544 let lsb = (sig_base >> 11) & 1;
545 let g = (sig_base >> 10) & 1;
546 let r = (sig_base >> 9) & 1;
547 let s = sig_base & 1;
548
549 let r_or_s = r | s;
550 let tie_or_gt = r_or_s | lsb;
551 let round_up = g & tie_or_gt;
552
553 let mant_trunc = sig_base >> 11;
554 let mant_rounded = mant_trunc.wrapping_add(round_up);
555
556 let overflow = (mant_rounded >> 53) & 1;
557 let overflow_msb = if overflow != 0 { 1u64 << 63 } else { 0 };
558 let mant_final_53 = if overflow_msb != 0 {
559 mant_rounded >> 1
560 } else {
561 mant_rounded
562 };
563 let exp_after = exp_base.wrapping_add(overflow);
564
565 (mant_final_53, exp_after, overflow_msb)
566 }
567
568 pub fn ref_fp64_pack_finite_or_inf(
569 sign_msb: u64,
570 mant_final_53: u64,
571 exp_after_round: u64,
572 stayed_sub_mask_msb: u64,
573 ) -> u64 {
574 let sign_hi = sign_msb & (1u64 << 63);
575 let frac = mant_final_53 & ((1u64 << 52) - 1);
576
577 let packed_sub = sign_hi | frac;
578 let exp_sh = exp_after_round << 52;
579 let packed_norm = sign_hi | exp_sh | frac;
580
581 let finite_packed = if stayed_sub_mask_msb != 0 {
582 packed_sub
583 } else {
584 packed_norm
585 };
586
587 let finite_or_zero = if mant_final_53 == 0 {
589 sign_hi
590 } else {
591 finite_packed
592 };
593
594 let overflow_to_inf = exp_after_round >= 0x7FF;
595 if overflow_to_inf {
596 let inf_payload = 0x7FFu64 << 52;
597 sign_hi | inf_payload
598 } else {
599 finite_or_zero
600 }
601 }
602
603 #[test]
604 fn test_bit_lsb() {
605 let builder = CircuitBuilder::new();
606 let input = builder.add_inout();
607 let output0 = bit_lsb(&builder, input, 0);
608 let output1 = bit_lsb(&builder, input, 1);
609 let output63 = bit_lsb(&builder, input, 63);
610 let expected0 = builder.add_inout();
611 let expected1 = builder.add_inout();
612 let expected63 = builder.add_inout();
613 builder.assert_eq("test_output0", output0, expected0);
614 builder.assert_eq("test_output1", output1, expected1);
615 builder.assert_eq("test_output63", output63, expected63);
616
617 let circuit = builder.build();
618
619 let test_cases = [
620 (0b101, 1, 0, 0), (0b110, 0, 1, 0), (0x8000000000000001, 1, 0, 1), ];
624
625 for (val, exp0, exp1, exp63) in test_cases {
626 let mut w = circuit.new_witness_filler();
627 w[input] = Word(val);
628 w[expected0] = Word(exp0);
629 w[expected1] = Word(exp1);
630 w[expected63] = Word(exp63);
631
632 circuit.populate_wire_witness(&mut w).unwrap();
633 let cs = circuit.constraint_system();
634 cs.verify(&w.into_value_vec()).unwrap();
635 }
636 }
637
638 #[test]
639 fn test_clz64() {
640 let builder = CircuitBuilder::new();
641 let input = builder.add_inout();
642 let output = clz64(&builder, input);
643 let expected = builder.add_inout();
644 builder.assert_eq("test_output", output, expected);
645
646 let circuit = builder.build();
647
648 let test_cases = [
649 (0x8000000000000000u64, 0), (0x4000000000000000u64, 1), (0x0000000000000001u64, 63), (0xFFFFFFFFFFFFFFFFu64, 0), (0x0000000000008000u64, 48), ];
655
656 for (val, expected_clz) in test_cases {
657 let mut w = circuit.new_witness_filler();
658 w[input] = Word(val);
659 w[expected] = Word(expected_clz);
660
661 circuit.populate_wire_witness(&mut w).unwrap();
662 let cs = circuit.constraint_system();
663 cs.verify(&w.into_value_vec()).unwrap();
664 }
665 }
666
667 #[test]
668 fn test_var_shl() {
669 let builder = CircuitBuilder::new();
670 let input = builder.add_inout();
671 let shift = builder.add_inout();
672 let output = var_shl(&builder, input, shift);
673 let expected = builder.add_inout();
674 builder.assert_eq("test_output", output, expected);
675
676 let circuit = builder.build();
677
678 let test_cases = [
679 (1, 0, 1), (1, 1, 2), (1, 8, 256), (0xFF, 4, 0xFF0), (1, 64, 1), ];
685
686 for (val, shift_amt, expected_result) in test_cases {
687 let mut w = circuit.new_witness_filler();
688 w[input] = Word(val);
689 w[shift] = Word(shift_amt);
690 w[expected] = Word(expected_result);
691
692 circuit.populate_wire_witness(&mut w).unwrap();
693 let cs = circuit.constraint_system();
694 cs.verify(&w.into_value_vec()).unwrap();
695 }
696 }
697
698 #[test]
699 fn test_var_shr_with_sticky() {
700 let builder = CircuitBuilder::new();
701 let input = builder.add_inout();
702 let shift = builder.add_inout();
703 let (output, sticky) = var_shr_with_sticky(&builder, input, shift, false);
704 let expected_out = builder.add_inout();
705 let expected_sticky = builder.add_inout();
706 builder.assert_eq("test_output", output, expected_out);
707 builder.assert_eq("test_sticky", sticky, expected_sticky);
708
709 let circuit = builder.build();
710
711 let test_cases = [
712 (8, 1, 4, 0), (7, 1, 3, 1), (0xFF, 4, 0xF, 1), (0xF0, 4, 0xF, 0), ];
717
718 for (val, shift_amt, expected_result, expected_sticky_val) in test_cases {
719 let mut w = circuit.new_witness_filler();
720 w[input] = Word(val);
721 w[shift] = Word(shift_amt);
722 w[expected_out] = Word(expected_result);
723 w[expected_sticky] = Word(expected_sticky_val);
724
725 circuit.populate_wire_witness(&mut w).unwrap();
726 let cs = circuit.constraint_system();
727 cs.verify(&w.into_value_vec()).unwrap();
728 }
729 }
730
731 #[test]
732 fn test_fp64_sig53_and_exp() {
733 let test_values = vec![1.0f64, -1.0f64, 2.0f64, f64::MIN_POSITIVE, 0.5f64];
736
737 for val in test_values {
738 let builder = CircuitBuilder::new();
739 let input = builder.add_inout();
740 let parts = fp64_unpack(&builder, input);
741 let (sig, exp_eff) = fp64_sig53_and_exp(&builder, &parts);
742 let expected_sig = builder.add_inout();
743 let expected_exp = builder.add_inout();
744
745 builder.assert_eq("sig", sig, expected_sig);
746 builder.assert_eq("exp_eff", exp_eff, expected_exp);
747
748 let circuit = builder.build();
749 let mut w = circuit.new_witness_filler();
750 w[input] = Word(val.to_bits());
751
752 let bits = val.to_bits();
754 let exp = (bits >> 52) & 0x7FF;
755 let frac = bits & ((1u64 << 52) - 1);
756 let is_norm = exp != 0 && exp != 0x7FF;
757
758 let (exp_sig, exp_exp) = if is_norm {
759 ((1u64 << 52) | frac, exp)
760 } else {
761 (frac, 1)
762 };
763
764 w[expected_sig] = Word(exp_sig);
765 w[expected_exp] = Word(exp_exp);
766
767 circuit.populate_wire_witness(&mut w).unwrap();
768 let cs = circuit.constraint_system();
769 cs.verify(&w.into_value_vec()).unwrap();
770 }
771 }
772
773 #[test]
774 fn test_shr128_to_u64_const() {
775 let test_cases = [
776 (0x0123456789ABCDEFu64, 0xFEDCBA9876543210u64, 8),
778 (0xFFFFFFFFFFFFFFFFu64, 0x0000000000000000u64, 32),
779 (0x0000000000000000u64, 0xFFFFFFFFFFFFFFFFu64, 16),
780 (0x8000000000000000u64, 0x0000000000000001u64, 1),
781 (0x1234567890ABCDEFu64, 0x1111111111111111u64, 4),
782 ];
783
784 for (hi, lo, shift) in test_cases {
785 let builder = CircuitBuilder::new();
786 let hi_wire = builder.add_inout();
787 let lo_wire = builder.add_inout();
788 let result = shr128_to_u64_const(&builder, hi_wire, lo_wire, shift);
789 let expected_wire = builder.add_inout();
790 builder.assert_eq("shr128_result", result, expected_wire);
791
792 let circuit = builder.build();
793 let mut w = circuit.new_witness_filler();
794 w[hi_wire] = Word(hi);
795 w[lo_wire] = Word(lo);
796
797 let p = ((hi as u128) << 64) | (lo as u128);
799 let expected = (p >> shift) as u64;
800 w[expected_wire] = Word(expected);
801
802 circuit.populate_wire_witness(&mut w).unwrap();
803 let cs = circuit.constraint_system();
804 cs.verify(&w.into_value_vec()).unwrap();
805 }
806 }
807
808 #[test]
809 fn test_sticky_from_low_k() {
810 let test_cases = [
811 (0x0000000000000000u64, 4, 0), (0x0000000000000001u64, 1, 1), (0x0000000000000002u64, 1, 0), (0x0000000000000002u64, 2, 1), (0x000000000000000Fu64, 4, 1), (0x0000000000000010u64, 4, 0), (0x00000000000000FFu64, 8, 1), (0x0000000000000100u64, 8, 0), ];
821
822 for (value, k, expected_val) in test_cases {
823 let builder = CircuitBuilder::new();
824 let input = builder.add_inout();
825 let result = sticky_from_low_k(&builder, input, k);
826 let expected = builder.add_inout();
827 builder.assert_eq("sticky_result", result, expected);
828
829 let circuit = builder.build();
830 let mut w = circuit.new_witness_filler();
831 w[input] = Word(value);
832 w[expected] = Word(expected_val);
833
834 circuit.populate_wire_witness(&mut w).unwrap();
835 let cs = circuit.constraint_system();
836 cs.verify(&w.into_value_vec()).unwrap();
837 }
838 }
839
840 #[test]
841 fn test_fp64_unpack() {
842 let test_values = vec![
843 0.0f64,
844 -0.0f64,
845 1.0f64,
846 -1.0f64,
847 f64::INFINITY,
848 f64::NEG_INFINITY,
849 f64::NAN,
850 f64::MIN_POSITIVE,
851 ];
852
853 for val in test_values {
854 let builder = CircuitBuilder::new();
855 let input = builder.add_inout();
856 let result = fp64_unpack(&builder, input);
857
858 let expected_sign = builder.add_inout();
860 let expected_exp = builder.add_inout();
861 let expected_frac = builder.add_inout();
862 let expected_is_nan = builder.add_inout();
863 let expected_is_inf = builder.add_inout();
864 let expected_is_norm = builder.add_inout();
865
866 let bool_mask = builder.add_constant(Word::MSB_ONE);
868 builder.assert_eq("sign", builder.band(result.sign, bool_mask), expected_sign);
869 builder.assert_eq("exp", result.exp, expected_exp);
870 builder.assert_eq("frac", result.frac, expected_frac);
871 builder.assert_eq("is_nan", builder.band(result.is_nan, bool_mask), expected_is_nan);
872 builder.assert_eq("is_inf", builder.band(result.is_inf, bool_mask), expected_is_inf);
873 builder.assert_eq("is_norm", builder.band(result.is_norm, bool_mask), expected_is_norm);
874
875 let circuit = builder.build();
876 let mut w = circuit.new_witness_filler();
877 w[input] = Word(val.to_bits());
878
879 let result = ref_fp64_unpack(val.to_bits());
881
882 w[expected_sign] = Word(result.sign);
883 w[expected_exp] = Word(result.exp);
884 w[expected_frac] = Word(result.frac);
885 w[expected_is_nan] = Word(result.is_nan);
886 w[expected_is_inf] = Word(result.is_inf);
887 w[expected_is_norm] = Word(result.is_norm);
888
889 circuit.populate_wire_witness(&mut w).unwrap();
890 let cs = circuit.constraint_system();
891 cs.verify(&w.into_value_vec()).unwrap();
892 }
893 }
894
895 #[test]
896 fn test_fp64_underflow_shift() {
897 let test_cases = [
898 (0x8000000000000000u64, 5), (0x8000000000000000u64, 0), (0x4000000000000000u64, -5i64 as u64), ];
903
904 for (res_sig, res_exp) in test_cases {
905 let builder = CircuitBuilder::new();
906 let res_sig_wire = builder.add_inout();
907 let res_exp_wire = builder.add_inout();
908
909 let (sig_round_base, exp_round_base, exp_lt_1) =
910 fp64_underflow_shift(&builder, res_sig_wire, res_exp_wire);
911
912 let expected_sig = builder.add_inout();
913 let expected_exp = builder.add_inout();
914 let expected_lt1 = builder.add_inout();
915 let bool_mask = builder.add_constant(Word::MSB_ONE);
916
917 builder.assert_eq("sig_round_base", sig_round_base, expected_sig);
918 builder.assert_eq("exp_round_base", exp_round_base, expected_exp);
919 builder.assert_eq("exp_lt_1", builder.band(exp_lt_1, bool_mask), expected_lt1);
920
921 let circuit = builder.build();
922 let mut w = circuit.new_witness_filler();
923 w[res_sig_wire] = Word(res_sig);
924 w[res_exp_wire] = Word(res_exp);
925
926 let (ref_sig, ref_exp, ref_lt1) = ref_fp64_underflow_shift(res_sig, res_exp);
927 w[expected_sig] = Word(ref_sig);
928 w[expected_exp] = Word(ref_exp);
929 w[expected_lt1] = Word(ref_lt1);
930
931 circuit.populate_wire_witness(&mut w).unwrap();
932 let cs = circuit.constraint_system();
933 cs.verify(&w.into_value_vec()).unwrap();
934 }
935 }
936
937 pub fn f64_bits_semantic_eq(a_bits: u64, b_bits: u64) -> bool {
939 let a = f64::from_bits(a_bits);
940 let b = f64::from_bits(b_bits);
941 if a.is_nan() && b.is_nan() {
942 true
943 } else {
944 a_bits == b_bits
945 }
946 }
947
948 #[test]
949 fn test_fp64_round_rne() {
950 let test_cases = [
951 (0x8000000000000000u64, 1023), (0x8000000000000800u64, 1023), (0x8000000000001800u64, 1023), (0x8000000000000C00u64, 1023), ];
958
959 for (sig_base, exp_base) in test_cases {
960 let builder = CircuitBuilder::new();
961 let sig_base_wire = builder.add_inout();
962 let exp_base_wire = builder.add_inout();
963
964 let (mant_final_53, exp_after_round, mant_overflow_mask) =
965 fp64_round_rne(&builder, sig_base_wire, exp_base_wire);
966
967 let expected_mant = builder.add_inout();
968 let expected_exp = builder.add_inout();
969 let expected_overflow = builder.add_inout();
970
971 builder.assert_eq("mant_final_53", mant_final_53, expected_mant);
972 builder.assert_eq("exp_after_round", exp_after_round, expected_exp);
973 builder.assert_eq("mant_overflow_mask", mant_overflow_mask, expected_overflow);
974
975 let circuit = builder.build();
976 let mut w = circuit.new_witness_filler();
977 w[sig_base_wire] = Word(sig_base);
978 w[exp_base_wire] = Word(exp_base);
979
980 let (ref_mant, ref_exp, ref_overflow) = ref_fp64_round_rne(sig_base, exp_base);
981 w[expected_mant] = Word(ref_mant);
982 w[expected_exp] = Word(ref_exp);
983 w[expected_overflow] = Word(ref_overflow);
984
985 circuit.populate_wire_witness(&mut w).unwrap();
986 let cs = circuit.constraint_system();
987 cs.verify(&w.into_value_vec()).unwrap();
988 }
989 }
990}