1use core::{
11 cmp::{max, min},
12 ops::{Mul, MulAssign},
13};
14
15use crate::{
16 abs_bits, abs_bits_sticky, exp_bits, f256, left_adj_signif, norm_bit,
17 signif, BigUInt, BinEncAnySpecial, HiLo, EMAX, EMIN, EXP_BIAS, EXP_BITS,
18 EXP_MAX, FRACTION_BITS, HI_ABS_MASK, HI_FRACTION_BIAS, HI_FRACTION_BITS,
19 HI_FRACTION_MASK, HI_SIGN_MASK, INF_HI, MAX_HI, SIGNIFICAND_BITS,
20 TOTAL_BITS, U256, U512,
21};
22
23#[inline]
24#[allow(clippy::cast_possible_truncation)]
25fn mul_signifs(x: &U256, y: &U256) -> (U256, u32, u32) {
26 debug_assert!(x.hi.0 >= HI_SIGN_MASK);
27 debug_assert!(y.hi.0 >= HI_SIGN_MASK);
28 let (lo, mut hi) = x.widening_mul(y);
29 let carry = (hi.hi.0 >= HI_SIGN_MASK) as u32;
30 let shift = EXP_BITS - 1 + carry;
31 let rem = hi.rem_pow2(shift).lo;
32 let rnd_bits = (rem.0 >> (shift - 2)) as u32
33 | (rem.0 > (1 << (shift - 1))) as u32
34 | !lo.is_zero() as u32;
35 hi >>= shift;
36 (hi, carry, rnd_bits)
37}
38
39#[allow(clippy::cast_sign_loss)]
40#[allow(clippy::cast_possible_wrap)]
41#[allow(clippy::cast_possible_truncation)]
42pub(crate) fn mul_abs_finite(
43 abs_bits_x: &U256,
44 abs_bits_y: &U256,
45) -> (U256, u32) {
46 let mut exp_bits_x = exp_bits(abs_bits_x) as i32;
48 let norm_bit_x = norm_bit(abs_bits_x) as i32;
49 let (mut norm_signif_x, norm_shift_x) = left_adj_signif(abs_bits_x);
50 let mut exp_bits_y = exp_bits(abs_bits_y) as i32;
51 let norm_bit_y = norm_bit(abs_bits_y) as i32;
52 let (mut norm_signif_y, norm_shift_y) = left_adj_signif(abs_bits_y);
53
54 let (mut signif_z, carry, mut rnd_bits) =
56 mul_signifs(&norm_signif_x, &norm_signif_y);
57 const EMIN_EXTRA_SHIFT_BIAS: i32 = EMIN + 2 * EXP_BITS as i32;
58 let mut exp_bits_z_minus_1 = (exp_bits_x - norm_bit_x)
59 + (exp_bits_y - norm_bit_y)
60 - (norm_shift_x + norm_shift_y) as i32
61 + EMIN_EXTRA_SHIFT_BIAS
62 + carry as i32;
63
64 if exp_bits_z_minus_1 >= (EXP_MAX - 1) as i32 {
67 return (U256::new(INF_HI, 0), 0_u32);
68 }
69
70 if exp_bits_z_minus_1 < 0 {
73 let shift = exp_bits_z_minus_1.unsigned_abs();
74 if shift > SIGNIFICAND_BITS + 1 {
75 return (U256::ZERO, 0_u32);
77 }
78 if shift > 0 {
79 match shift {
81 1 => {
82 rnd_bits = (((signif_z.lo.0 & 1) as u32) << 1)
83 | (rnd_bits != 0) as u32;
84 }
85 2 => {
86 rnd_bits =
87 ((signif_z.lo.0 & 3) as u32) | (rnd_bits != 0) as u32;
88 }
89 3..=127 => {
90 let rem = signif_z.rem_pow2(shift).lo.0;
91 rnd_bits = (rem >> (shift - 2)) as u32
92 | (rem > (1_u128 << (shift - 1))) as u32
93 | (rnd_bits != 0) as u32;
94 }
95 _ => {
96 let rem = signif_z.rem_pow2(shift);
97 rnd_bits = (rem >> (shift - 2)).lo.0 as u32
98 | (rem > (U256::ONE << (shift - 1))) as u32
99 | (rnd_bits != 0) as u32;
100 }
101 }
102 signif_z >>= shift;
103 }
104 exp_bits_z_minus_1 = 0;
105 }
106
107 let abs_bits_z = U256::new(
109 signif_z.hi.0 + ((exp_bits_z_minus_1 as u128) << HI_FRACTION_BITS),
110 signif_z.lo.0,
111 );
112 (abs_bits_z, rnd_bits)
113}
114
115#[allow(clippy::cast_possible_truncation)]
117#[allow(clippy::cast_possible_wrap)]
118#[allow(clippy::cast_sign_loss)]
119#[inline]
120pub(crate) fn mul(x: f256, y: f256) -> f256 {
121 let sign_bits_hi_z = (x.bits.hi.0 ^ y.bits.hi.0) & HI_SIGN_MASK;
123 let mut abs_bits_x = abs_bits(&x);
124 let mut abs_bits_y = abs_bits(&y);
125 let abs_bits_sticky_x = abs_bits_sticky(&abs_bits_x);
130 let abs_bits_sticky_y = abs_bits_sticky(&abs_bits_y);
131 if (abs_bits_sticky_x, abs_bits_sticky_y).any_special() {
132 let max_abs_bits_sticky = max(abs_bits_sticky_x, abs_bits_sticky_y);
133 let min_abs_bits_sticky = min(abs_bits_sticky_x, abs_bits_sticky_y);
134 if min_abs_bits_sticky == 0 {
135 if max_abs_bits_sticky < INF_HI {
137 return f256 {
139 bits: U256::new(sign_bits_hi_z, 0),
140 };
141 };
142 if max_abs_bits_sticky == INF_HI {
143 return f256::NAN;
145 }
146 }
147 if max_abs_bits_sticky > INF_HI {
148 return f256::NAN;
150 }
151 return f256 {
153 bits: U256::new(sign_bits_hi_z | INF_HI, 0),
154 };
155 }
156
157 let (mut bits_z, rnd_bits) = mul_abs_finite(&abs_bits_x, &abs_bits_y);
159 bits_z.hi.0 |= sign_bits_hi_z;
160
161 if rnd_bits > 0b10 || (rnd_bits == 0b10 && bits_z.lo.is_odd()) {
163 bits_z.incr();
164 }
165 f256 { bits: bits_z }
166}
167
168impl Mul for f256 {
169 type Output = Self;
170
171 fn mul(self, rhs: Self) -> Self::Output {
172 mul(self, rhs)
173 }
174}
175
176forward_ref_binop!(impl Mul, mul);
177
178forward_op_assign!(impl MulAssign, mul_assign, Mul, mul);