Skip to content

Commit 9bee3d7

Browse files
committed
Convert between BigInts, BigUints, ints, and uints
Previously, conversion to ints, uints, and BigUints clamped the value within the range of that datatype. With this commit, conversion overflows fail the task. To handle overflows gracefully, use the new to_*_opt() methods.
1 parent dfb04d9 commit 9bee3d7

File tree

1 file changed

+100
-53
lines changed

1 file changed

+100
-53
lines changed

src/libextra/num/bigint.rs

Lines changed: 100 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ impl Integer for BigUint {
465465
impl IntConvertible for BigUint {
466466
#[inline]
467467
fn to_int(&self) -> int {
468-
num::min(self.to_uint(), int::max_value as uint) as int
468+
self.to_int_opt().expect("BigUint conversion would overflow int")
469469
}
470470

471471
#[inline]
@@ -577,19 +577,38 @@ impl BigUint {
577577
}
578578

579579

580-
/// Converts this big integer into a uint, returning the uint::max_value if
581-
/// it's too large to fit in a uint.
580+
/// Converts this BigUint into a uint, failing if the conversion
581+
/// would overflow.
582582
#[inline]
583583
pub fn to_uint(&self) -> uint {
584+
self.to_uint_opt().expect("BigUint conversion would overflow uint")
585+
}
586+
587+
/// Converts this BigUint into a uint, unless it would overflow.
588+
#[inline]
589+
pub fn to_uint_opt(&self) -> Option<uint> {
584590
match self.data.len() {
585-
0 => 0,
586-
1 => self.data[0] as uint,
587-
2 => BigDigit::to_uint(self.data[1], self.data[0]),
588-
_ => uint::max_value
591+
0 => Some(0),
592+
1 => Some(self.data[0] as uint),
593+
2 => Some(BigDigit::to_uint(self.data[1], self.data[0])),
594+
_ => None
589595
}
590596
}
591597

592-
/// Converts this BigUint into a positively-signed BigInt.
598+
// Converts this BigUint into an int, unless it would overflow.
599+
pub fn to_int_opt(&self) -> Option<int> {
600+
self.to_uint_opt().chain(|n| {
601+
// If top bit of uint is set, it's too large to convert to
602+
// int.
603+
if (n >> (2*BigDigit::bits - 1) != 0) {
604+
None
605+
} else {
606+
Some(n as int)
607+
}
608+
})
609+
}
610+
611+
/// Converts this BigUint into a BigInt.
593612
#[inline]
594613
pub fn to_bigint(&self) -> BigInt {
595614
BigInt::from_biguint(Plus, self.clone())
@@ -1016,12 +1035,7 @@ impl Integer for BigInt {
10161035
impl IntConvertible for BigInt {
10171036
#[inline]
10181037
fn to_int(&self) -> int {
1019-
match self.sign {
1020-
Plus => num::min(self.to_uint(), int::max_value as uint) as int,
1021-
Zero => 0,
1022-
Minus => num::min((-self).to_uint(),
1023-
(int::max_value as uint) + 1) as int
1024-
}
1038+
self.to_int_opt().expect("BigInt conversion would overflow int")
10251039
}
10261040

10271041
#[inline]
@@ -1100,22 +1114,55 @@ impl BigInt {
11001114
.map_move(|bu| BigInt::from_biguint(sign, bu));
11011115
}
11021116
1117+
/// Converts this BigInt into a uint, failing if the conversion
1118+
/// would overflow.
11031119
#[inline]
11041120
pub fn to_uint(&self) -> uint {
1121+
self.to_uint_opt().expect("BigInt conversion would overflow uint")
1122+
}
1123+
1124+
/// Converts this BigInt into a uint, unless it would overflow.
1125+
#[inline]
1126+
pub fn to_uint_opt(&self) -> Option<uint> {
11051127
match self.sign {
1106-
Plus => self.data.to_uint(),
1107-
Zero => 0,
1108-
Minus => 0
1128+
Plus => self.data.to_uint_opt(),
1129+
Zero => Some(0),
1130+
Minus => None
11091131
}
11101132
}
11111133

1112-
/// Converts this BigInt into a BigUint. Negative BigInts are
1113-
/// converted to zero-valued BigUints.
1134+
/// Converts this BigInt into an int, unless it would overflow.
1135+
pub fn to_int_opt(&self) -> Option<int> {
1136+
match self.sign {
1137+
Plus => self.data.to_int_opt(),
1138+
Zero => Some(0),
1139+
Minus => self.data.to_uint_opt().chain(|n| {
1140+
let m: uint = 1 << (2*BigDigit::bits-1);
1141+
if (n > m) {
1142+
None
1143+
} else if (n == m) {
1144+
Some(int::min_value)
1145+
} else {
1146+
Some(-(n as int))
1147+
}
1148+
})
1149+
}
1150+
}
1151+
1152+
/// Converts this BigInt into a BigUint, failing if BigInt is
1153+
/// negative.
11141154
#[inline]
11151155
pub fn to_biguint(&self) -> BigUint {
1156+
self.to_biguint_opt().expect("negative BigInt cannot convert to BigUint")
1157+
}
1158+
1159+
/// Converts this BigInt into a BigUint, if it's not negative.
1160+
#[inline]
1161+
pub fn to_biguint_opt(&self) -> Option<BigUint> {
11161162
match self.sign {
1117-
Plus => self.data.clone(),
1118-
_ => Zero::zero()
1163+
Plus => Some(self.data.clone()),
1164+
Zero => Some(Zero::zero()),
1165+
Minus => None
11191166
}
11201167
}
11211168
}
@@ -1273,9 +1320,9 @@ mod biguint_tests {
12731320
check(~[ 0, 1], ((uint::max_value >> BigDigit::bits) + 1) as int);
12741321
check(~[-1, -1 >> 1], int::max_value);
12751322

1276-
assert_eq!(BigUint::new(~[0, -1]).to_int(), int::max_value);
1277-
assert_eq!(BigUint::new(~[0, 0, 1]).to_int(), int::max_value);
1278-
assert_eq!(BigUint::new(~[0, 0, -1]).to_int(), int::max_value);
1323+
assert_eq!(BigUint::new(~[0, -1]).to_int_opt(), None);
1324+
assert_eq!(BigUint::new(~[0, 0, 1]).to_int_opt(), None);
1325+
assert_eq!(BigUint::new(~[0, 0, -1]).to_int_opt(), None);
12791326
}
12801327

12811328
#[test]
@@ -1293,8 +1340,8 @@ mod biguint_tests {
12931340
check(~[ 0, -1], uint::max_value << BigDigit::bits);
12941341
check(~[-1, -1], uint::max_value);
12951342

1296-
assert_eq!(BigUint::new(~[0, 0, 1]).to_uint(), uint::max_value);
1297-
assert_eq!(BigUint::new(~[0, 0, -1]).to_uint(), uint::max_value);
1343+
assert_eq!(BigUint::new(~[0, 0, 1]).to_uint_opt(), None);
1344+
assert_eq!(BigUint::new(~[0, 0, -1]).to_uint_opt(), None);
12981345
}
12991346

13001347
#[test]
@@ -1304,7 +1351,8 @@ mod biguint_tests {
13041351
assert_eq!(n.to_bigint().to_biguint(), n);
13051352
}
13061353
check(Zero::zero(), Zero::zero());
1307-
check(BigUint::from_uint(637), BigInt::from_uint(637));
1354+
check(BigUint::new(~[1,2,3]),
1355+
BigInt::from_biguint(Plus, BigUint::new(~[1,2,3])));
13081356
}
13091357

13101358
static sum_triples: &'static [(&'static [BigDigit],
@@ -1683,22 +1731,21 @@ mod bigint_tests {
16831731
Plus, BigUint::from_uint(int::max_value as uint)
16841732
), int::max_value);
16851733

1686-
assert!(BigInt::from_biguint(
1734+
assert_eq!(BigInt::from_biguint(
16871735
Plus, BigUint::from_uint(int::max_value as uint + 1)
1688-
).to_int() == int::max_value);
1689-
assert!(BigInt::from_biguint(
1736+
).to_int_opt(), None);
1737+
assert_eq!(BigInt::from_biguint(
16901738
Plus, BigUint::new(~[1, 2, 3])
1691-
).to_int() == int::max_value);
1739+
).to_int_opt(), None);
16921740

16931741
check(BigInt::from_biguint(
1694-
Minus, BigUint::from_uint(-int::min_value as uint)
1742+
Minus, BigUint::new(~[0, 1<<(BigDigit::bits-1)])
16951743
), int::min_value);
1696-
assert!(BigInt::from_biguint(
1697-
Minus, BigUint::from_uint(-int::min_value as uint + 1)
1698-
).to_int() == int::min_value);
1699-
assert!(BigInt::from_biguint(
1700-
Minus, BigUint::new(~[1, 2, 3])
1701-
).to_int() == int::min_value);
1744+
assert_eq!(BigInt::from_biguint(
1745+
Minus, BigUint::new(~[1, 1<<(BigDigit::bits-1)])
1746+
).to_int_opt(), None);
1747+
assert_eq!(BigInt::from_biguint(
1748+
Minus, BigUint::new(~[1, 2, 3])).to_int_opt(), None);
17021749
}
17031750

17041751
#[test]
@@ -1714,31 +1761,31 @@ mod bigint_tests {
17141761
check(
17151762
BigInt::from_biguint(Plus, BigUint::from_uint(uint::max_value)),
17161763
uint::max_value);
1717-
assert!(BigInt::from_biguint(
1718-
Plus, BigUint::new(~[1, 2, 3])
1719-
).to_uint() == uint::max_value);
1764+
assert_eq!(BigInt::from_biguint(
1765+
Plus, BigUint::new(~[1, 2, 3])).to_uint_opt(), None);
17201766

1721-
assert!(BigInt::from_biguint(
1722-
Minus, BigUint::from_uint(uint::max_value)
1723-
).to_uint() == 0);
1724-
assert!(BigInt::from_biguint(
1725-
Minus, BigUint::new(~[1, 2, 3])
1726-
).to_uint() == 0);
1767+
assert_eq!(BigInt::from_biguint(
1768+
Minus, BigUint::from_uint(uint::max_value)).to_uint_opt(), None);
1769+
assert_eq!(BigInt::from_biguint(
1770+
Minus, BigUint::new(~[1, 2, 3])).to_uint_opt(), None);
17271771
}
17281772

17291773
#[test]
17301774
fn test_convert_to_biguint() {
1731-
fn check(n: BigInt, ans_1: BigUint, ans_2: BigInt) {
1775+
fn check(n: BigInt, ans_1: BigUint) {
17321776
assert_eq!(n.to_biguint(), ans_1);
1733-
assert_eq!(n.to_biguint().to_bigint(), ans_2);
1777+
assert_eq!(n.to_biguint().to_bigint(), n);
17341778
}
17351779
let zero: BigInt = Zero::zero();
17361780
let unsigned_zero: BigUint = Zero::zero();
1737-
let positive: BigInt = BigInt::from_uint(637);
1781+
let positive = BigInt::from_biguint(
1782+
Plus, BigUint::new(~[1,2,3]));
17381783
let negative = -positive;
1739-
check(zero.clone(), unsigned_zero.clone(), zero.clone());
1740-
check(positive.clone(), BigUint::from_uint(637), positive);
1741-
check(negative, unsigned_zero, zero);
1784+
1785+
check(zero, unsigned_zero);
1786+
check(positive, BigUint::new(~[1,2,3]));
1787+
1788+
assert_eq!(negative.to_biguint_opt(), None);
17421789
}
17431790

17441791
static sum_triples: &'static [(&'static [BigDigit],

0 commit comments

Comments
 (0)