Skip to content

Commit 8c63b02

Browse files
add Int::saturating_mul
Signed-off-by: Andrew Whitehead <cywolf@gmail.com>
1 parent 40a1c5a commit 8c63b02

File tree

1 file changed

+58
-0
lines changed

1 file changed

+58
-0
lines changed

src/int/mul.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,19 @@ impl<const LIMBS: usize> Int<LIMBS> {
7777
Self::new_from_abs_sign(*lo, lhs_sgn.xor(rhs_sgn)).and_choice(is_some)
7878
}
7979

80+
/// Multiply `self` by `rhs`, saturating at the numeric bounds instead of overflowing.
81+
pub const fn saturating_mul<const RHS_LIMBS: usize>(&self, rhs: &Int<RHS_LIMBS>) -> Self {
82+
let (abs_lhs, lhs_sgn) = self.abs_sign();
83+
let (abs_rhs, rhs_sgn) = rhs.abs_sign();
84+
let maybe_res = abs_lhs.checked_mul(&abs_rhs);
85+
let (lo, is_some) = maybe_res.components_ref();
86+
let is_neg = lhs_sgn.xor(rhs_sgn);
87+
let bound = Self::select(&Self::MAX, &Self::MIN, is_neg);
88+
Self::new_from_abs_sign(*lo, is_neg)
89+
.and_choice(is_some)
90+
.unwrap_or(bound)
91+
}
92+
8093
/// Multiply `self` by `rhs`, wrapping the result in case of overflow.
8194
/// This is equivalent to `(self * rhs) % (Uint::<LIMBS>::MAX + 1)`.
8295
pub const fn wrapping_mul<const RHS_LIMBS: usize>(&self, rhs: &Int<RHS_LIMBS>) -> Self {
@@ -330,6 +343,51 @@ mod tests {
330343
);
331344
}
332345

346+
#[test]
347+
fn test_saturating_mul() {
348+
// wrapping
349+
let a = 0xFFFFFFFB7B63198EF870DF1F90D9BD9Eu128 as i128;
350+
let b = 0xF20C29FA87B356AA3B4C05C4F9C24B4Au128 as i128;
351+
assert_eq!(a.saturating_mul(b), i128::MAX);
352+
assert_eq!(
353+
I128::from_i128(a).saturating_mul(&I128::from_i128(b)),
354+
I128::MAX
355+
);
356+
357+
// no wrapping
358+
let c = -12345i64;
359+
assert_eq!(
360+
I128::from_i128(a).saturating_mul(&I128::from_i64(c)),
361+
I128::from_i128(a.saturating_mul(c as i128))
362+
);
363+
364+
// core case
365+
assert_eq!(i8::MAX.saturating_mul(2), i8::MAX);
366+
assert_eq!(i8::MAX.saturating_mul(-2), i8::MIN);
367+
assert_eq!(i64::MAX.saturating_mul(2), i64::MAX);
368+
assert_eq!(i64::MAX.saturating_mul(-2), i64::MIN);
369+
assert_eq!(I128::MAX.saturating_mul(&I128::from_i64(2i64)), I128::MAX);
370+
assert_eq!(I128::MAX.saturating_mul(&I128::from_i64(-2i64)), I128::MIN);
371+
372+
let x = -197044252290277702i64;
373+
let y = -2631691865753118366;
374+
assert_eq!(x.saturating_mul(y), i64::MAX);
375+
assert_eq!(I64::from_i64(x).saturating_mul(&I64::from_i64(y)), I64::MAX);
376+
377+
let x = -86027672844719838068326470675019902915i128;
378+
let y = 21188806580823612823777395451044967239i128;
379+
assert_eq!(x.saturating_mul(y), i128::MIN);
380+
assert_eq!(x.saturating_mul(-y), i128::MAX);
381+
assert_eq!(
382+
I128::from_i128(x).saturating_mul(&I128::from_i128(y)),
383+
I128::MIN
384+
);
385+
assert_eq!(
386+
I128::from_i128(x).saturating_mul(&I128::from_i128(-y)),
387+
I128::MAX
388+
);
389+
}
390+
333391
#[test]
334392
fn test_concatenating_mul() {
335393
assert_eq!(

0 commit comments

Comments
 (0)