Skip to content

Commit 51a9a2d

Browse files
committed
Overhaul overflowing multiplication impls
1 parent 2dcc72d commit 51a9a2d

File tree

2 files changed

+71
-58
lines changed

2 files changed

+71
-58
lines changed

src/float/conv.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ macro_rules! int_to_float {
1111
let mant_dig = <$fty>::SIGNIFICAND_BITS + 1;
1212
let exponent_bias = <$fty>::EXPONENT_BIAS;
1313

14-
let n = <$ity>::BITS;
14+
let n = <$ity as Int>::BITS;
1515
let (s, a) = i.extract_sign();
1616
let mut a = a;
1717

@@ -21,7 +21,7 @@ macro_rules! int_to_float {
2121
// exponent
2222
let mut e = sd - 1;
2323

24-
if <$ity>::BITS < mant_dig {
24+
if <$ity as Int>::BITS < mant_dig {
2525
return <$fty>::from_parts(
2626
s,
2727
(e + exponent_bias) as <$fty as Float>::Int,
@@ -165,7 +165,7 @@ macro_rules! float_to_int {
165165
let f = $f;
166166
let fixint_min = <$ity>::min_value();
167167
let fixint_max = <$ity>::max_value();
168-
let fixint_bits = <$ity>::BITS as usize;
168+
let fixint_bits = <$ity as Int>::BITS as usize;
169169
let fixint_unsigned = fixint_min == 0;
170170

171171
let sign_bit = <$fty>::SIGN_MASK;

src/int/mul.rs

Lines changed: 68 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
use core::ops;
2-
3-
use int::Int;
41
use int::LargeInt;
2+
use int::{DInt, HInt, Int};
53

64
trait Mul: LargeInt {
75
fn mul(self, other: Self) -> Self {
@@ -29,59 +27,72 @@ trait Mul: LargeInt {
2927
impl Mul for u64 {}
3028
impl Mul for i128 {}
3129

32-
trait Mulo: Int + ops::Neg<Output = Self> {
33-
fn mulo(self, other: Self, overflow: &mut i32) -> Self {
34-
*overflow = 0;
35-
let result = self.wrapping_mul(other);
36-
if self == Self::min_value() {
37-
if other != Self::ZERO && other != Self::ONE {
38-
*overflow = 1;
30+
pub(crate) trait UMulo: Int + DInt {
31+
fn mulo(self, rhs: Self) -> (Self, bool) {
32+
match (self.hi().is_zero(), rhs.hi().is_zero()) {
33+
// overflow is guaranteed
34+
(false, false) => (self.wrapping_mul(rhs), true),
35+
(true, false) => {
36+
let mul_lo = self.lo().widen_mul(rhs.lo());
37+
let mul_hi = self.lo().widen_mul(rhs.hi());
38+
let (mul, o) = mul_lo.overflowing_add(mul_hi.lo().widen_hi());
39+
(mul, o || !mul_hi.hi().is_zero())
3940
}
40-
return result;
41-
}
42-
if other == Self::min_value() {
43-
if self != Self::ZERO && self != Self::ONE {
44-
*overflow = 1;
41+
(false, true) => {
42+
let mul_lo = rhs.lo().widen_mul(self.lo());
43+
let mul_hi = rhs.lo().widen_mul(self.hi());
44+
let (mul, o) = mul_lo.overflowing_add(mul_hi.lo().widen_hi());
45+
(mul, o || !mul_hi.hi().is_zero())
4546
}
46-
return result;
47+
// overflow is guaranteed to not happen, and use a smaller widening multiplication
48+
(true, true) => (self.lo().widen_mul(rhs.lo()), false),
4749
}
50+
}
51+
}
4852

49-
let sa = self >> (Self::BITS - 1);
50-
let abs_a = (self ^ sa) - sa;
51-
let sb = other >> (Self::BITS - 1);
52-
let abs_b = (other ^ sb) - sb;
53-
let two = Self::ONE + Self::ONE;
54-
if abs_a < two || abs_b < two {
55-
return result;
56-
}
57-
if sa == sb {
58-
if abs_a > Self::max_value().aborting_div(abs_b) {
59-
*overflow = 1;
53+
impl UMulo for u32 {}
54+
impl UMulo for u64 {}
55+
impl UMulo for u128 {}
56+
57+
macro_rules! impl_signed_mulo {
58+
($fn:ident, $iD:ident, $uD:ident) => {
59+
fn $fn(lhs: $iD, rhs: $iD) -> ($iD, bool) {
60+
let mut lhs = lhs;
61+
let mut rhs = rhs;
62+
// the test against `mul_neg` below fails without this early return
63+
if lhs == 0 || rhs == 0 {
64+
return (0, false);
6065
}
61-
} else {
62-
if abs_a > Self::min_value().aborting_div(-abs_b) {
63-
*overflow = 1;
66+
67+
let lhs_neg = lhs < 0;
68+
let rhs_neg = rhs < 0;
69+
if lhs_neg {
70+
lhs = lhs.wrapping_neg();
6471
}
65-
}
66-
result
67-
}
68-
}
72+
if rhs_neg {
73+
rhs = rhs.wrapping_neg();
74+
}
75+
let mul_neg = lhs_neg != rhs_neg;
6976

70-
impl Mulo for i32 {}
71-
impl Mulo for i64 {}
72-
impl Mulo for i128 {}
77+
let (mul, o) = (lhs as $uD).mulo(rhs as $uD);
78+
let mut mul = mul as $iD;
7379

74-
trait UMulo: Int {
75-
fn mulo(self, other: Self, overflow: &mut i32) -> Self {
76-
*overflow = 0;
77-
let result = self.wrapping_mul(other);
78-
if self > Self::max_value().aborting_div(other) {
79-
*overflow = 1;
80+
if mul_neg {
81+
mul = mul.wrapping_neg();
82+
}
83+
if (mul < 0) != mul_neg {
84+
// this one check happens to catch all edge cases related to `$iD::MIN`
85+
(mul, true)
86+
} else {
87+
(mul, o)
88+
}
8089
}
81-
result
82-
}
90+
};
8391
}
84-
impl UMulo for u128 {}
92+
93+
impl_signed_mulo!(i32_overflowing_mul, i32, u32);
94+
impl_signed_mulo!(i64_overflowing_mul, i64, u64);
95+
impl_signed_mulo!(i128_overflowing_mul, i128, u128);
8596

8697
intrinsics! {
8798
#[maybe_use_optimized_c_shim]
@@ -95,27 +106,29 @@ intrinsics! {
95106
}
96107

97108
pub extern "C" fn __mulosi4(a: i32, b: i32, oflow: &mut i32) -> i32 {
98-
a.mulo(b, oflow)
109+
let (mul, o) = i32_overflowing_mul(a, b);
110+
*oflow = o as i32;
111+
mul
99112
}
100113

101114
pub extern "C" fn __mulodi4(a: i64, b: i64, oflow: &mut i32) -> i64 {
102-
a.mulo(b, oflow)
115+
let (mul, o) = i64_overflowing_mul(a, b);
116+
*oflow = o as i32;
117+
mul
103118
}
104119

105120
#[unadjusted_on_win64]
106121
pub extern "C" fn __muloti4(a: i128, b: i128, oflow: &mut i32) -> i128 {
107-
a.mulo(b, oflow)
122+
let (mul, o) = i128_overflowing_mul(a, b);
123+
*oflow = o as i32;
124+
mul
108125
}
109126

110127
pub extern "C" fn __rust_i128_mulo(a: i128, b: i128) -> (i128, bool) {
111-
let mut oflow = 0;
112-
let r = __muloti4(a, b, &mut oflow);
113-
(r, oflow != 0)
128+
i128_overflowing_mul(a, b)
114129
}
115130

116131
pub extern "C" fn __rust_u128_mulo(a: u128, b: u128) -> (u128, bool) {
117-
let mut oflow = 0;
118-
let r = a.mulo(b, &mut oflow);
119-
(r, oflow != 0)
132+
a.mulo(b)
120133
}
121134
}

0 commit comments

Comments
 (0)