Add basic conversions for i128 and u128

This commit is contained in:
Josh Stone 2018-05-15 18:01:14 -07:00
parent 275ba23cee
commit 04252b7716
7 changed files with 334 additions and 28 deletions

View File

@ -11,6 +11,7 @@ repository = "https://github.com/rust-num/num-bigint"
version = "0.2.0-git"
publish = false
readme = "README.md"
build = "build.rs"
[[bench]]
name = "bigint"
@ -28,11 +29,11 @@ name = "shootout-pidigits"
[dependencies]
[dependencies.num-integer]
version = "0.1.36"
version = "0.1.38"
default-features = false
[dependencies.num-traits]
version = "0.2.1"
version = "0.2.4"
default-features = false
features = ["std"]
@ -52,3 +53,4 @@ version = "0.4"
[features]
default = []
i128 = ["num-integer/i128", "num-traits/i128"]

View File

@ -22,6 +22,12 @@ and this to your crate root:
extern crate num_bigint;
```
## Features
Implementations for `i128` and `u128` are only available with Rust 1.26 and
later. The build script automatically detects this, but you can make it
mandatory by enabling the `i128` crate feature.
## Releases
Release notes are available in [RELEASES.md](RELEASES.md).

35
build.rs Normal file
View File

@ -0,0 +1,35 @@
use std::env;
use std::io::Write;
use std::process::{Command, Stdio};
fn main() {
if probe("fn main() { 0i128; }") {
println!("cargo:rustc-cfg=has_i128");
} else if env::var_os("CARGO_FEATURE_I128").is_some() {
panic!("i128 support was not detected!");
}
}
/// Test if a code snippet can be compiled
fn probe(code: &str) -> bool {
let rustc = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
let out_dir = env::var_os("OUT_DIR").expect("environment variable OUT_DIR");
let mut child = Command::new(rustc)
.arg("--out-dir")
.arg(out_dir)
.arg("--emit=obj")
.arg("-")
.stdin(Stdio::piped())
.spawn()
.expect("rustc probe");
child
.stdin
.as_mut()
.expect("rustc stdin")
.write_all(code.as_bytes())
.expect("write rustc stdin");
child.wait().expect("rustc probe").success()
}

View File

@ -7,7 +7,9 @@ use std::fmt;
use std::mem;
use std::cmp::Ordering::{self, Less, Greater, Equal};
use std::{i64, u64};
#[allow(unused_imports)]
#[cfg(has_i128)]
use std::{i128, u128};
#[allow(deprecated, unused_imports)]
use std::ascii::AsciiExt;
use std::iter::{Product, Sum};
@ -1811,6 +1813,27 @@ impl ToPrimitive for BigInt {
}
}
#[inline]
#[cfg(has_i128)]
fn to_i128(&self) -> Option<i128> {
match self.sign {
Plus => self.data.to_i128(),
NoSign => Some(0),
Minus => {
self.data.to_u128().and_then(|n| {
let m: u128 = 1 << 127;
if n < m {
Some(-(n as i128))
} else if n == m {
Some(i128::MIN)
} else {
None
}
})
}
}
}
#[inline]
fn to_u64(&self) -> Option<u64> {
match self.sign {
@ -1820,6 +1843,16 @@ impl ToPrimitive for BigInt {
}
}
#[inline]
#[cfg(has_i128)]
fn to_u128(&self) -> Option<u128> {
match self.sign {
Plus => self.data.to_u128(),
NoSign => Some(0),
Minus => None,
}
}
#[inline]
fn to_f32(&self) -> Option<f32> {
self.data.to_f32().map(|n| {
@ -1849,11 +1882,23 @@ impl FromPrimitive for BigInt {
Some(BigInt::from(n))
}
#[inline]
#[cfg(has_i128)]
fn from_i128(n: i128) -> Option<BigInt> {
Some(BigInt::from(n))
}
#[inline]
fn from_u64(n: u64) -> Option<BigInt> {
Some(BigInt::from(n))
}
#[inline]
#[cfg(has_i128)]
fn from_u128(n: u128) -> Option<BigInt> {
Some(BigInt::from(n))
}
#[inline]
fn from_f64(n: f64) -> Option<BigInt> {
if n >= 0.0 {
@ -1879,6 +1924,22 @@ impl From<i64> for BigInt {
}
}
#[cfg(has_i128)]
impl From<i128> for BigInt {
#[inline]
fn from(n: i128) -> Self {
if n >= 0 {
BigInt::from(n as u128)
} else {
let u = u128::MAX - (n as u128) + 1;
BigInt {
sign: Minus,
data: BigUint::from(u),
}
}
}
}
macro_rules! impl_bigint_from_int {
($T:ty) => {
impl From<$T> for BigInt {
@ -1909,6 +1970,21 @@ impl From<u64> for BigInt {
}
}
#[cfg(has_i128)]
impl From<u128> for BigInt {
#[inline]
fn from(n: u128) -> Self {
if n > 0 {
BigInt {
sign: Plus,
data: BigUint::from(n),
}
} else {
BigInt::zero()
}
}
}
macro_rules! impl_bigint_from_uint {
($T:ty) => {
impl From<$T> for BigInt {
@ -2040,11 +2116,17 @@ impl_to_bigint!(i8, FromPrimitive::from_i8);
impl_to_bigint!(i16, FromPrimitive::from_i16);
impl_to_bigint!(i32, FromPrimitive::from_i32);
impl_to_bigint!(i64, FromPrimitive::from_i64);
#[cfg(has_i128)]
impl_to_bigint!(i128, FromPrimitive::from_i128);
impl_to_bigint!(usize, FromPrimitive::from_usize);
impl_to_bigint!(u8, FromPrimitive::from_u8);
impl_to_bigint!(u16, FromPrimitive::from_u16);
impl_to_bigint!(u32, FromPrimitive::from_u32);
impl_to_bigint!(u64, FromPrimitive::from_u64);
#[cfg(has_i128)]
impl_to_bigint!(u128, FromPrimitive::from_u128);
impl_to_bigint!(f32, FromPrimitive::from_f32);
impl_to_bigint!(f64, FromPrimitive::from_f64);

View File

@ -11,7 +11,7 @@ use std::mem;
use std::cmp::Ordering::{self, Less, Greater, Equal};
use std::{f32, f64};
use std::{u8, u64};
#[allow(unused_imports)]
#[allow(deprecated, unused_imports)]
use std::ascii::AsciiExt;
#[cfg(feature = "serde")]
@ -1059,14 +1059,13 @@ fn high_bits_to_u64(v: &BigUint) -> u64 {
impl ToPrimitive for BigUint {
#[inline]
fn to_i64(&self) -> Option<i64> {
self.to_u64().and_then(|n| {
// If top bit of u64 is set, it's too large to convert to i64.
if n >> 63 == 0 {
Some(n as i64)
} else {
None
}
})
self.to_u64().as_ref().and_then(u64::to_i64)
}
#[inline]
#[cfg(has_i128)]
fn to_i128(&self) -> Option<i128> {
self.to_u128().as_ref().and_then(u128::to_i128)
}
#[inline]
@ -1086,6 +1085,24 @@ impl ToPrimitive for BigUint {
Some(ret)
}
#[inline]
#[cfg(has_i128)]
fn to_u128(&self) -> Option<u128> {
let mut ret: u128 = 0;
let mut bits = 0;
for i in self.data.iter() {
if bits >= 128 {
return None;
}
ret += (*i as u128) << bits;
bits += big_digit::BITS;
}
Some(ret)
}
#[inline]
fn to_f32(&self) -> Option<f32> {
let mantissa = high_bits_to_u64(self);
@ -1131,11 +1148,27 @@ impl FromPrimitive for BigUint {
}
}
#[inline]
#[cfg(has_i128)]
fn from_i128(n: i128) -> Option<BigUint> {
if n >= 0 {
Some(BigUint::from(n as u128))
} else {
None
}
}
#[inline]
fn from_u64(n: u64) -> Option<BigUint> {
Some(BigUint::from(n))
}
#[inline]
#[cfg(has_i128)]
fn from_u128(n: u128) -> Option<BigUint> {
Some(BigUint::from(n))
}
#[inline]
fn from_f64(mut n: f64) -> Option<BigUint> {
// handle NAN, INFINITY, NEG_INFINITY
@ -1182,6 +1215,21 @@ impl From<u64> for BigUint {
}
}
#[cfg(has_i128)]
impl From<u128> for BigUint {
#[inline]
fn from(mut n: u128) -> Self {
let mut ret: BigUint = Zero::zero();
while n != 0 {
ret.data.push(n as BigDigit);
n >>= big_digit::BITS;
}
ret
}
}
macro_rules! impl_biguint_from_uint {
($T:ty) => {
impl From<$T> for BigUint {
@ -1227,11 +1275,17 @@ impl_to_biguint!(i8, FromPrimitive::from_i8);
impl_to_biguint!(i16, FromPrimitive::from_i16);
impl_to_biguint!(i32, FromPrimitive::from_i32);
impl_to_biguint!(i64, FromPrimitive::from_i64);
#[cfg(has_i128)]
impl_to_biguint!(i128, FromPrimitive::from_i128);
impl_to_biguint!(usize, FromPrimitive::from_usize);
impl_to_biguint!(u8, FromPrimitive::from_u8);
impl_to_biguint!(u16, FromPrimitive::from_u16);
impl_to_biguint!(u32, FromPrimitive::from_u32);
impl_to_biguint!(u64, FromPrimitive::from_u64);
#[cfg(has_i128)]
impl_to_biguint!(u128, FromPrimitive::from_u128);
impl_to_biguint!(f32, FromPrimitive::from_f32);
impl_to_biguint!(f64, FromPrimitive::from_f64);

View File

@ -12,6 +12,8 @@ use std::{f32, f64};
use std::{i8, i16, i32, i64, isize};
use std::iter::repeat;
use std::{u8, u16, u32, u64, usize};
#[cfg(has_i128)]
use std::{i128, u128};
use std::ops::Neg;
use std::hash::{BuildHasher, Hasher, Hash};
use std::collections::hash_map::RandomState;
@ -240,16 +242,52 @@ fn test_convert_i64() {
assert_eq!((i64::MAX as u64 + 1).to_bigint().unwrap().to_i64(), None);
assert_eq!(BigInt::from_biguint(Plus, BigUint::new(vec![1, 2, 3, 4, 5])).to_i64(),
None);
assert_eq!(
BigInt::from_biguint(Plus, BigUint::new(vec![1, 2, 3, 4, 5])).to_i64(),
None
);
assert_eq!(BigInt::from_biguint(Minus,
BigUint::new(vec![1, 0, 0, 1 << 31]))
.to_i64(),
None);
assert_eq!(
BigInt::from_biguint(Minus, BigUint::new(vec![1, 0, 0, 1 << 31])).to_i64(),
None
);
assert_eq!(BigInt::from_biguint(Minus, BigUint::new(vec![1, 2, 3, 4, 5])).to_i64(),
None);
assert_eq!(
BigInt::from_biguint(Minus, BigUint::new(vec![1, 2, 3, 4, 5])).to_i64(),
None
);
}
#[test]
#[cfg(has_i128)]
fn test_convert_i128() {
fn check(b1: BigInt, i: i128) {
let b2: BigInt = FromPrimitive::from_i128(i).unwrap();
assert!(b1 == b2);
assert!(b1.to_i128().unwrap() == i);
}
check(Zero::zero(), 0);
check(One::one(), 1);
check(i128::MIN.to_bigint().unwrap(), i128::MIN);
check(i128::MAX.to_bigint().unwrap(), i128::MAX);
assert_eq!((i128::MAX as u128 + 1).to_bigint().unwrap().to_i128(), None);
assert_eq!(
BigInt::from_biguint(Plus, BigUint::new(vec![1, 2, 3, 4, 5])).to_i128(),
None
);
assert_eq!(
BigInt::from_biguint(Minus, BigUint::new(vec![1, 0, 0, 1 << 31])).to_i128(),
None
);
assert_eq!(
BigInt::from_biguint(Minus, BigUint::new(vec![1, 2, 3, 4, 5])).to_i128(),
None
);
}
#[test]
@ -265,13 +303,44 @@ fn test_convert_u64() {
check(u64::MIN.to_bigint().unwrap(), u64::MIN);
check(u64::MAX.to_bigint().unwrap(), u64::MAX);
assert_eq!(BigInt::from_biguint(Plus, BigUint::new(vec![1, 2, 3, 4, 5])).to_u64(),
None);
assert_eq!(
BigInt::from_biguint(Plus, BigUint::new(vec![1, 2, 3, 4, 5])).to_u64(),
None
);
let max_value: BigUint = FromPrimitive::from_u64(u64::MAX).unwrap();
assert_eq!(BigInt::from_biguint(Minus, max_value).to_u64(), None);
assert_eq!(BigInt::from_biguint(Minus, BigUint::new(vec![1, 2, 3, 4, 5])).to_u64(),
None);
assert_eq!(
BigInt::from_biguint(Minus, BigUint::new(vec![1, 2, 3, 4, 5])).to_u64(),
None
);
}
#[test]
#[cfg(has_i128)]
fn test_convert_u128() {
fn check(b1: BigInt, u: u128) {
let b2: BigInt = FromPrimitive::from_u128(u).unwrap();
assert!(b1 == b2);
assert!(b1.to_u128().unwrap() == u);
}
check(Zero::zero(), 0);
check(One::one(), 1);
check(u128::MIN.to_bigint().unwrap(), u128::MIN);
check(u128::MAX.to_bigint().unwrap(), u128::MAX);
assert_eq!(
BigInt::from_biguint(Plus, BigUint::new(vec![1, 2, 3, 4, 5])).to_u128(),
None
);
let max_value: BigUint = FromPrimitive::from_u128(u128::MAX).unwrap();
assert_eq!(BigInt::from_biguint(Minus, max_value).to_u128(), None);
assert_eq!(
BigInt::from_biguint(Minus, BigUint::new(vec![1, 2, 3, 4, 5])).to_u128(),
None
);
}
#[test]
@ -455,14 +524,15 @@ fn test_convert_from_uint() {
assert_eq!(BigInt::from($ty::one()), BigInt::one());
assert_eq!(BigInt::from($ty::MAX - $ty::one()), $max - BigInt::one());
assert_eq!(BigInt::from($ty::MAX), $max);
}
};
}
check!(u8, BigInt::from_slice(Plus, &[u8::MAX as u32]));
check!(u16, BigInt::from_slice(Plus, &[u16::MAX as u32]));
check!(u32, BigInt::from_slice(Plus, &[u32::MAX]));
check!(u64,
BigInt::from_slice(Plus, &[u32::MAX, u32::MAX]));
check!(u64, BigInt::from_slice(Plus, &[u32::MAX, u32::MAX]));
#[cfg(has_i128)]
check!(u128, BigInt::from_slice(Plus, &[u32::MAX, u32::MAX, u32::MAX, u32::MAX]));
check!(usize, BigInt::from(usize::MAX as u64));
}
@ -492,6 +562,10 @@ fn test_convert_from_int() {
check!(i64,
BigInt::from_slice(Minus, &[0, 1 << 31]),
BigInt::from_slice(Plus, &[u32::MAX, i32::MAX as u32]));
#[cfg(has_i128)]
check!(i128,
BigInt::from_slice(Minus, &[0, 0, 0, 1 << 31]),
BigInt::from_slice(Plus, &[u32::MAX, u32::MAX, u32::MAX, i32::MAX as u32]));
check!(isize,
BigInt::from(isize::MIN as i64),
BigInt::from(isize::MAX as i64));

View File

@ -14,6 +14,8 @@ use std::i64;
use std::iter::repeat;
use std::str::FromStr;
use std::{u8, u16, u32, u64, usize};
#[cfg(has_i128)]
use std::{i128, u128};
use std::hash::{BuildHasher, Hasher, Hash};
use std::collections::hash_map::RandomState;
@ -490,6 +492,31 @@ fn test_convert_i64() {
assert_eq!(BigUint::new(vec![N1, N1, N1]).to_i64(), None);
}
#[test]
#[cfg(has_i128)]
fn test_convert_i128() {
fn check(b1: BigUint, i: i128) {
let b2: BigUint = FromPrimitive::from_i128(i).unwrap();
assert_eq!(b1, b2);
assert_eq!(b1.to_i128().unwrap(), i);
}
check(Zero::zero(), 0);
check(One::one(), 1);
check(i128::MAX.to_biguint().unwrap(), i128::MAX);
check(BigUint::new(vec![]), 0);
check(BigUint::new(vec![1]), 1);
check(BigUint::new(vec![N1]), (1 << 32) - 1);
check(BigUint::new(vec![0, 1]), 1 << 32);
check(BigUint::new(vec![N1, N1, N1, N1 >> 1]), i128::MAX);
assert_eq!(i128::MIN.to_biguint(), None);
assert_eq!(BigUint::new(vec![N1, N1, N1, N1]).to_i128(), None);
assert_eq!(BigUint::new(vec![0, 0, 0, 0, 1]).to_i128(), None);
assert_eq!(BigUint::new(vec![N1, N1, N1, N1, N1]).to_i128(), None);
}
// `DoubleBigDigit` size dependent
#[test]
fn test_convert_u64() {
@ -514,6 +541,30 @@ fn test_convert_u64() {
assert_eq!(BigUint::new(vec![N1, N1, N1]).to_u64(), None);
}
#[test]
#[cfg(has_i128)]
fn test_convert_u128() {
fn check(b1: BigUint, u: u128) {
let b2: BigUint = FromPrimitive::from_u128(u).unwrap();
assert_eq!(b1, b2);
assert_eq!(b1.to_u128().unwrap(), u);
}
check(Zero::zero(), 0);
check(One::one(), 1);
check(u128::MIN.to_biguint().unwrap(), u128::MIN);
check(u128::MAX.to_biguint().unwrap(), u128::MAX);
check(BigUint::new(vec![]), 0);
check(BigUint::new(vec![1]), 1);
check(BigUint::new(vec![N1]), (1 << 32) - 1);
check(BigUint::new(vec![0, 1]), 1 << 32);
check(BigUint::new(vec![N1, N1, N1, N1]), u128::MAX);
assert_eq!(BigUint::new(vec![0, 0, 0, 0, 1]).to_u128(), None);
assert_eq!(BigUint::new(vec![N1, N1, N1, N1, N1]).to_u128(), None);
}
#[test]
fn test_convert_f32() {
fn check(b1: &BigUint, f: f32) {
@ -677,6 +728,8 @@ fn test_convert_from_uint() {
check!(u16, BigUint::from_slice(&[u16::MAX as u32]));
check!(u32, BigUint::from_slice(&[u32::MAX]));
check!(u64, BigUint::from_slice(&[u32::MAX, u32::MAX]));
#[cfg(has_i128)]
check!(u128, BigUint::from_slice(&[u32::MAX, u32::MAX, u32::MAX, u32::MAX]));
check!(usize, BigUint::from(usize::MAX as u64));
}
@ -1317,7 +1370,7 @@ fn test_from_str_radix() {
#[test]
fn test_all_str_radix() {
#[allow(unused_imports)]
#[allow(deprecated, unused_imports)]
use std::ascii::AsciiExt;
let n = BigUint::new((0..10).collect());