bigint: fix float conversions
The default implementations of to_f32, to_f64, from_32 and from_f64 are limited to numbers fitting in i64 but BigInt can of course be much bigger. The default to_f32 also has double rounding. from_f32 and from_f64 have undefined behaviour if the float is too big to fit in an i64. This fixes these issues and keeps the rounding consistant with other float to int conversions. Also add ToBigUint and ToBigInt implementations for f32 and f64.
This commit is contained in:
parent
283a2e192d
commit
a480c7ca6c
419
src/bigint.rs
419
src/bigint.rs
|
@ -68,11 +68,13 @@ use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Rem, Shl, Shr, Sub};
|
||||||
use std::str::{self, FromStr};
|
use std::str::{self, FromStr};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::cmp::Ordering::{self, Less, Greater, Equal};
|
use std::cmp::Ordering::{self, Less, Greater, Equal};
|
||||||
|
use std::{f32, f64};
|
||||||
use std::{u8, i64, u64};
|
use std::{u8, i64, u64};
|
||||||
|
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
use traits::{ToPrimitive, FromPrimitive};
|
use traits::{ToPrimitive, FromPrimitive};
|
||||||
|
use traits::Float;
|
||||||
|
|
||||||
use {Num, Unsigned, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, Signed, Zero, One};
|
use {Num, Unsigned, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, Signed, Zero, One};
|
||||||
use self::Sign::{Minus, NoSign, Plus};
|
use self::Sign::{Minus, NoSign, Plus};
|
||||||
|
@ -1168,6 +1170,71 @@ impl ToPrimitive for BigUint {
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `DoubleBigDigit` size dependent
|
||||||
|
#[inline]
|
||||||
|
fn to_f32(&self) -> Option<f32> {
|
||||||
|
match self.data.len() {
|
||||||
|
0 => Some(f32::zero()),
|
||||||
|
1 => Some(self.data[0] as f32),
|
||||||
|
len => {
|
||||||
|
// prevent overflow of exponant
|
||||||
|
if len > (f32::MAX_EXP as usize) / big_digit::BITS {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let exponant = (len - 2) * big_digit::BITS;
|
||||||
|
// we need 25 significant digits, 24 to be stored and 1 for rounding
|
||||||
|
// this gives at least 33 significant digits
|
||||||
|
let mantissa = big_digit::to_doublebigdigit(self.data[len - 1], self.data[len - 2]);
|
||||||
|
// this cast handles rounding
|
||||||
|
let ret = (mantissa as f32) * 2.0.powi(exponant as i32);
|
||||||
|
if ret.is_infinite() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// `DoubleBigDigit` size dependent
|
||||||
|
#[inline]
|
||||||
|
fn to_f64(&self) -> Option<f64> {
|
||||||
|
match self.data.len() {
|
||||||
|
0 => Some(f64::zero()),
|
||||||
|
1 => Some(self.data[0] as f64),
|
||||||
|
2 => Some(big_digit::to_doublebigdigit(self.data[1], self.data[0]) as f64),
|
||||||
|
len => {
|
||||||
|
// this will prevent any overflow of exponant
|
||||||
|
if len > (f64::MAX_EXP as usize) / big_digit::BITS {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let mut exponant = (len - 2) * big_digit::BITS;
|
||||||
|
let mut mantissa = big_digit::to_doublebigdigit(self.data[len - 1], self.data[len - 2]);
|
||||||
|
// we need at least 54 significant bit digits, 53 to be stored and 1 for rounding
|
||||||
|
// so we take enough from the next BigDigit to make it up if needed
|
||||||
|
let needed = (f64::MANTISSA_DIGITS as usize) + 1;
|
||||||
|
let bits = (2 * big_digit::BITS) - (mantissa.leading_zeros() as usize);
|
||||||
|
if needed > bits {
|
||||||
|
let diff = needed - bits;
|
||||||
|
mantissa <<= diff;
|
||||||
|
exponant -= diff;
|
||||||
|
let mut x = self.data[len - 3];
|
||||||
|
x >>= big_digit::BITS - diff;
|
||||||
|
mantissa |= x as u64;
|
||||||
|
}
|
||||||
|
// this cast handles rounding
|
||||||
|
let ret = (mantissa as f64) * 2.0.powi(exponant as i32);
|
||||||
|
if ret.is_infinite() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromPrimitive for BigUint {
|
impl FromPrimitive for BigUint {
|
||||||
|
@ -1184,6 +1251,41 @@ impl FromPrimitive for BigUint {
|
||||||
fn from_u64(n: u64) -> Option<BigUint> {
|
fn from_u64(n: u64) -> Option<BigUint> {
|
||||||
Some(BigUint::from(n))
|
Some(BigUint::from(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_f32(n: f32) -> Option<BigUint> {
|
||||||
|
BigUint::from_f64(n as f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_f64(mut n: f64) -> Option<BigUint> {
|
||||||
|
// handle NAN, INFINITY, NEG_INFINITY
|
||||||
|
if !n.is_finite() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// match the rounding of casting from float to int
|
||||||
|
n = n.trunc();
|
||||||
|
|
||||||
|
// handle 0.x, -0.x
|
||||||
|
if n.is_zero() {
|
||||||
|
return Some(BigUint::zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
let (mantissa, exponent, sign) = Float::integer_decode(n);
|
||||||
|
|
||||||
|
if sign == -1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ret = BigUint::from(mantissa);
|
||||||
|
if exponent > 0 {
|
||||||
|
ret = ret << exponent as usize;
|
||||||
|
} else if exponent < 0 {
|
||||||
|
ret = ret >> (-exponent) as usize;
|
||||||
|
}
|
||||||
|
Some(ret)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u64> for BigUint {
|
impl From<u64> for BigUint {
|
||||||
|
@ -1261,6 +1363,8 @@ impl_to_biguint!(u8, FromPrimitive::from_u8);
|
||||||
impl_to_biguint!(u16, FromPrimitive::from_u16);
|
impl_to_biguint!(u16, FromPrimitive::from_u16);
|
||||||
impl_to_biguint!(u32, FromPrimitive::from_u32);
|
impl_to_biguint!(u32, FromPrimitive::from_u32);
|
||||||
impl_to_biguint!(u64, FromPrimitive::from_u64);
|
impl_to_biguint!(u64, FromPrimitive::from_u64);
|
||||||
|
impl_to_biguint!(f32, FromPrimitive::from_f32);
|
||||||
|
impl_to_biguint!(f64, FromPrimitive::from_f64);
|
||||||
|
|
||||||
// Extract bitwise digits that evenly divide BigDigit
|
// Extract bitwise digits that evenly divide BigDigit
|
||||||
fn to_bitwise_digits_le(u: &BigUint, bits: usize) -> Vec<u8> {
|
fn to_bitwise_digits_le(u: &BigUint, bits: usize) -> Vec<u8> {
|
||||||
|
@ -2123,6 +2227,16 @@ impl ToPrimitive for BigInt {
|
||||||
Minus => None
|
Minus => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_f32(&self) -> Option<f32> {
|
||||||
|
self.data.to_f32().map(|n| if self.sign == Minus { -n } else { n })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn to_f64(&self) -> Option<f64> {
|
||||||
|
self.data.to_f64().map(|n| if self.sign == Minus { -n } else { n })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromPrimitive for BigInt {
|
impl FromPrimitive for BigInt {
|
||||||
|
@ -2135,6 +2249,24 @@ impl FromPrimitive for BigInt {
|
||||||
fn from_u64(n: u64) -> Option<BigInt> {
|
fn from_u64(n: u64) -> Option<BigInt> {
|
||||||
Some(BigInt::from(n))
|
Some(BigInt::from(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_f32(n: f32) -> Option<BigInt> {
|
||||||
|
if n >= 0.0 {
|
||||||
|
BigUint::from_f32(n).map(|x| BigInt::from_biguint(Plus, x))
|
||||||
|
} else {
|
||||||
|
BigUint::from_f32(-n).map(|x| BigInt::from_biguint(Minus, x))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn from_f64(n: f64) -> Option<BigInt> {
|
||||||
|
if n >= 0.0 {
|
||||||
|
BigUint::from_f64(n).map(|x| BigInt::from_biguint(Plus, x))
|
||||||
|
} else {
|
||||||
|
BigUint::from_f64(-n).map(|x| BigInt::from_biguint(Minus, x))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<i64> for BigInt {
|
impl From<i64> for BigInt {
|
||||||
|
@ -2248,6 +2380,8 @@ impl_to_bigint!(u8, FromPrimitive::from_u8);
|
||||||
impl_to_bigint!(u16, FromPrimitive::from_u16);
|
impl_to_bigint!(u16, FromPrimitive::from_u16);
|
||||||
impl_to_bigint!(u32, FromPrimitive::from_u32);
|
impl_to_bigint!(u32, FromPrimitive::from_u32);
|
||||||
impl_to_bigint!(u64, FromPrimitive::from_u64);
|
impl_to_bigint!(u64, FromPrimitive::from_u64);
|
||||||
|
impl_to_bigint!(f32, FromPrimitive::from_f32);
|
||||||
|
impl_to_bigint!(f64, FromPrimitive::from_f64);
|
||||||
|
|
||||||
pub trait RandBigInt {
|
pub trait RandBigInt {
|
||||||
/// Generate a random `BigUint` of the given bit size.
|
/// Generate a random `BigUint` of the given bit size.
|
||||||
|
@ -2544,6 +2678,7 @@ mod biguint_tests {
|
||||||
use super::Sign::Plus;
|
use super::Sign::Plus;
|
||||||
|
|
||||||
use std::cmp::Ordering::{Less, Equal, Greater};
|
use std::cmp::Ordering::{Less, Equal, Greater};
|
||||||
|
use std::{f32, f64};
|
||||||
use std::i64;
|
use std::i64;
|
||||||
use std::iter::repeat;
|
use std::iter::repeat;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
@ -2552,6 +2687,7 @@ mod biguint_tests {
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use {Num, Zero, One, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv};
|
use {Num, Zero, One, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv};
|
||||||
use {ToPrimitive, FromPrimitive};
|
use {ToPrimitive, FromPrimitive};
|
||||||
|
use Float;
|
||||||
|
|
||||||
/// Assert that an op works for all val/ref combinations
|
/// Assert that an op works for all val/ref combinations
|
||||||
macro_rules! assert_op {
|
macro_rules! assert_op {
|
||||||
|
@ -3031,6 +3167,137 @@ mod biguint_tests {
|
||||||
assert_eq!(BigUint::new(vec!(N1, N1, N1)).to_u64(), None);
|
assert_eq!(BigUint::new(vec!(N1, N1, N1)).to_u64(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_convert_f32() {
|
||||||
|
fn check(b1: &BigUint, f: f32) {
|
||||||
|
let b2 = BigUint::from_f32(f).unwrap();
|
||||||
|
assert_eq!(b1, &b2);
|
||||||
|
assert_eq!(b1.to_f32().unwrap(), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
check(&BigUint::zero(), 0.0);
|
||||||
|
check(&BigUint::one(), 1.0);
|
||||||
|
check(&BigUint::from(u16::MAX), 2.0.powi(16) - 1.0);
|
||||||
|
check(&BigUint::from(1u64 << 32), 2.0.powi(32));
|
||||||
|
check(&BigUint::from_slice(&[0, 0, 1]), 2.0.powi(64));
|
||||||
|
check(&((BigUint::one() << 100) + (BigUint::one() << 123)), 2.0.powi(100) + 2.0.powi(123));
|
||||||
|
check(&(BigUint::one() << 127), 2.0.powi(127));
|
||||||
|
check(&(BigUint::from((1u64 << 24) - 1) << (128 - 24)), f32::MAX);
|
||||||
|
|
||||||
|
// keeping all 24 digits with the bits at different offsets to the BigDigits
|
||||||
|
let x: u32 = 0b00000000101111011111011011011101;
|
||||||
|
let mut f = x as f32;
|
||||||
|
let mut b = BigUint::from(x);
|
||||||
|
for _ in 0..64 {
|
||||||
|
check(&b, f);
|
||||||
|
f *= 2.0;
|
||||||
|
b = b << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this number when rounded to f64 then f32 isn't the same as when rounded straight to f32
|
||||||
|
let n: u64 = 0b0000000000111111111111111111111111011111111111111111111111111111;
|
||||||
|
assert!((n as f64) as f32 != n as f32);
|
||||||
|
assert_eq!(BigUint::from(n).to_f32(), Some(n as f32));
|
||||||
|
|
||||||
|
// test rounding up with the bits at different offsets to the BigDigits
|
||||||
|
let mut f = ((1u64 << 25) - 1) as f32;
|
||||||
|
let mut b = BigUint::from(1u64 << 25);
|
||||||
|
for _ in 0..64 {
|
||||||
|
assert_eq!(b.to_f32(), Some(f));
|
||||||
|
f *= 2.0;
|
||||||
|
b = b << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rounding
|
||||||
|
assert_eq!(BigUint::from_f32(-1.0), None);
|
||||||
|
assert_eq!(BigUint::from_f32(-0.99999), Some(BigUint::zero()));
|
||||||
|
assert_eq!(BigUint::from_f32(-0.5), Some(BigUint::zero()));
|
||||||
|
assert_eq!(BigUint::from_f32(-0.0), Some(BigUint::zero()));
|
||||||
|
assert_eq!(BigUint::from_f32(f32::MIN_POSITIVE / 2.0), Some(BigUint::zero()));
|
||||||
|
assert_eq!(BigUint::from_f32(f32::MIN_POSITIVE), Some(BigUint::zero()));
|
||||||
|
assert_eq!(BigUint::from_f32(0.5), Some(BigUint::zero()));
|
||||||
|
assert_eq!(BigUint::from_f32(0.99999), Some(BigUint::zero()));
|
||||||
|
assert_eq!(BigUint::from_f32(f32::consts::E), Some(BigUint::from(2u32)));
|
||||||
|
assert_eq!(BigUint::from_f32(f32::consts::PI), Some(BigUint::from(3u32)));
|
||||||
|
|
||||||
|
// special float values
|
||||||
|
assert_eq!(BigUint::from_f32(f32::NAN), None);
|
||||||
|
assert_eq!(BigUint::from_f32(f32::INFINITY), None);
|
||||||
|
assert_eq!(BigUint::from_f32(f32::NEG_INFINITY), None);
|
||||||
|
assert_eq!(BigUint::from_f32(f32::MIN), None);
|
||||||
|
|
||||||
|
// largest BigUint that will round to a finite f32 value
|
||||||
|
let big_num = (BigUint::one() << 128) - BigUint::one() - (BigUint::one() << (128 - 25));
|
||||||
|
assert_eq!(big_num.to_f32(), Some(f32::MAX));
|
||||||
|
assert_eq!((big_num + BigUint::one()).to_f32(), None);
|
||||||
|
|
||||||
|
assert_eq!(((BigUint::one() << 128) - BigUint::one()).to_f32(), None);
|
||||||
|
assert_eq!((BigUint::one() << 128).to_f32(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_convert_f64() {
|
||||||
|
fn check(b1: &BigUint, f: f64) {
|
||||||
|
let b2 = BigUint::from_f64(f).unwrap();
|
||||||
|
assert_eq!(b1, &b2);
|
||||||
|
assert_eq!(b1.to_f64().unwrap(), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
check(&BigUint::zero(), 0.0);
|
||||||
|
check(&BigUint::one(), 1.0);
|
||||||
|
check(&BigUint::from(u32::MAX), 2.0.powi(32) - 1.0);
|
||||||
|
check(&BigUint::from(1u64 << 32), 2.0.powi(32));
|
||||||
|
check(&BigUint::from_slice(&[0, 0, 1]), 2.0.powi(64));
|
||||||
|
check(&((BigUint::one() << 100) + (BigUint::one() << 152)), 2.0.powi(100) + 2.0.powi(152));
|
||||||
|
check(&(BigUint::one() << 1023), 2.0.powi(1023));
|
||||||
|
check(&(BigUint::from((1u64 << 53) - 1) << (1024 - 53)), f64::MAX);
|
||||||
|
|
||||||
|
// keeping all 53 digits with the bits at different offsets to the BigDigits
|
||||||
|
let x: u64 = 0b0000000000011110111110110111111101110111101111011111011011011101;
|
||||||
|
let mut f = x as f64;
|
||||||
|
let mut b = BigUint::from(x);
|
||||||
|
for _ in 0..128 {
|
||||||
|
check(&b, f);
|
||||||
|
f *= 2.0;
|
||||||
|
b = b << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// test rounding up with the bits at different offsets to the BigDigits
|
||||||
|
let mut f = ((1u64 << 54) - 1) as f64;
|
||||||
|
let mut b = BigUint::from(1u64 << 54);
|
||||||
|
for _ in 0..128 {
|
||||||
|
assert_eq!(b.to_f64(), Some(f));
|
||||||
|
f *= 2.0;
|
||||||
|
b = b << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rounding
|
||||||
|
assert_eq!(BigUint::from_f64(-1.0), None);
|
||||||
|
assert_eq!(BigUint::from_f64(-0.99999), Some(BigUint::zero()));
|
||||||
|
assert_eq!(BigUint::from_f64(-0.5), Some(BigUint::zero()));
|
||||||
|
assert_eq!(BigUint::from_f64(-0.0), Some(BigUint::zero()));
|
||||||
|
assert_eq!(BigUint::from_f64(f64::MIN_POSITIVE / 2.0), Some(BigUint::zero()));
|
||||||
|
assert_eq!(BigUint::from_f64(f64::MIN_POSITIVE), Some(BigUint::zero()));
|
||||||
|
assert_eq!(BigUint::from_f64(0.5), Some(BigUint::zero()));
|
||||||
|
assert_eq!(BigUint::from_f64(0.99999), Some(BigUint::zero()));
|
||||||
|
assert_eq!(BigUint::from_f64(f64::consts::E), Some(BigUint::from(2u32)));
|
||||||
|
assert_eq!(BigUint::from_f64(f64::consts::PI), Some(BigUint::from(3u32)));
|
||||||
|
|
||||||
|
// special float values
|
||||||
|
assert_eq!(BigUint::from_f64(f64::NAN), None);
|
||||||
|
assert_eq!(BigUint::from_f64(f64::INFINITY), None);
|
||||||
|
assert_eq!(BigUint::from_f64(f64::NEG_INFINITY), None);
|
||||||
|
assert_eq!(BigUint::from_f64(f64::MIN), None);
|
||||||
|
|
||||||
|
// largest BigUint that will round to a finite f64 value
|
||||||
|
let big_num = (BigUint::one() << 1024) - BigUint::one() - (BigUint::one() << (1024 - 54));
|
||||||
|
assert_eq!(big_num.to_f64(), Some(f64::MAX));
|
||||||
|
assert_eq!((big_num + BigUint::one()).to_f64(), None);
|
||||||
|
|
||||||
|
assert_eq!(((BigInt::one() << 1024) - BigInt::one()).to_f64(), None);
|
||||||
|
assert_eq!((BigUint::one() << 1024).to_f64(), None);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_convert_to_bigint() {
|
fn test_convert_to_bigint() {
|
||||||
fn check(n: BigUint, ans: BigInt) {
|
fn check(n: BigUint, ans: BigInt) {
|
||||||
|
@ -3585,6 +3852,7 @@ mod bigint_tests {
|
||||||
use super::Sign::{Minus, NoSign, Plus};
|
use super::Sign::{Minus, NoSign, Plus};
|
||||||
|
|
||||||
use std::cmp::Ordering::{Less, Equal, Greater};
|
use std::cmp::Ordering::{Less, Equal, Greater};
|
||||||
|
use std::{f32, f64};
|
||||||
use std::{i8, i16, i32, i64, isize};
|
use std::{i8, i16, i32, i64, isize};
|
||||||
use std::iter::repeat;
|
use std::iter::repeat;
|
||||||
use std::{u8, u16, u32, u64, usize};
|
use std::{u8, u16, u32, u64, usize};
|
||||||
|
@ -3593,6 +3861,7 @@ mod bigint_tests {
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
|
|
||||||
use {Zero, One, Signed, ToPrimitive, FromPrimitive, Num};
|
use {Zero, One, Signed, ToPrimitive, FromPrimitive, Num};
|
||||||
|
use Float;
|
||||||
|
|
||||||
/// Assert that an op works for all val/ref combinations
|
/// Assert that an op works for all val/ref combinations
|
||||||
macro_rules! assert_op {
|
macro_rules! assert_op {
|
||||||
|
@ -3793,6 +4062,156 @@ mod bigint_tests {
|
||||||
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]
|
||||||
|
fn test_convert_f32() {
|
||||||
|
fn check(b1: &BigInt, f: f32) {
|
||||||
|
let b2 = BigInt::from_f32(f).unwrap();
|
||||||
|
assert_eq!(b1, &b2);
|
||||||
|
assert_eq!(b1.to_f32().unwrap(), f);
|
||||||
|
let neg_b1 = -b1;
|
||||||
|
let neg_b2 = BigInt::from_f32(-f).unwrap();
|
||||||
|
assert_eq!(neg_b1, neg_b2);
|
||||||
|
assert_eq!(neg_b1.to_f32().unwrap(), -f);
|
||||||
|
}
|
||||||
|
|
||||||
|
check(&BigInt::zero(), 0.0);
|
||||||
|
check(&BigInt::one(), 1.0);
|
||||||
|
check(&BigInt::from(u16::MAX), 2.0.powi(16) - 1.0);
|
||||||
|
check(&BigInt::from(1u64 << 32), 2.0.powi(32));
|
||||||
|
check(&BigInt::from_slice(Plus, &[0, 0, 1]), 2.0.powi(64));
|
||||||
|
check(&((BigInt::one() << 100) + (BigInt::one() << 123)), 2.0.powi(100) + 2.0.powi(123));
|
||||||
|
check(&(BigInt::one() << 127), 2.0.powi(127));
|
||||||
|
check(&(BigInt::from((1u64 << 24) - 1) << (128 - 24)), f32::MAX);
|
||||||
|
|
||||||
|
// keeping all 24 digits with the bits at different offsets to the BigDigits
|
||||||
|
let x: u32 = 0b00000000101111011111011011011101;
|
||||||
|
let mut f = x as f32;
|
||||||
|
let mut b = BigInt::from(x);
|
||||||
|
for _ in 0..64 {
|
||||||
|
check(&b, f);
|
||||||
|
f *= 2.0;
|
||||||
|
b = b << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this number when rounded to f64 then f32 isn't the same as when rounded straight to f32
|
||||||
|
let mut n: i64 = 0b0000000000111111111111111111111111011111111111111111111111111111;
|
||||||
|
assert!((n as f64) as f32 != n as f32);
|
||||||
|
assert_eq!(BigInt::from(n).to_f32(), Some(n as f32));
|
||||||
|
n = -n;
|
||||||
|
assert!((n as f64) as f32 != n as f32);
|
||||||
|
assert_eq!(BigInt::from(n).to_f32(), Some(n as f32));
|
||||||
|
|
||||||
|
// test rounding up with the bits at different offsets to the BigDigits
|
||||||
|
let mut f = ((1u64 << 25) - 1) as f32;
|
||||||
|
let mut b = BigInt::from(1u64 << 25);
|
||||||
|
for _ in 0..64 {
|
||||||
|
assert_eq!(b.to_f32(), Some(f));
|
||||||
|
f *= 2.0;
|
||||||
|
b = b << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rounding
|
||||||
|
assert_eq!(BigInt::from_f32(-f32::consts::PI), Some(BigInt::from(-3i32)));
|
||||||
|
assert_eq!(BigInt::from_f32(-f32::consts::E), Some(BigInt::from(-2i32)));
|
||||||
|
assert_eq!(BigInt::from_f32(-0.99999), Some(BigInt::zero()));
|
||||||
|
assert_eq!(BigInt::from_f32(-0.5), Some(BigInt::zero()));
|
||||||
|
assert_eq!(BigInt::from_f32(-0.0), Some(BigInt::zero()));
|
||||||
|
assert_eq!(BigInt::from_f32(f32::MIN_POSITIVE / 2.0), Some(BigInt::zero()));
|
||||||
|
assert_eq!(BigInt::from_f32(f32::MIN_POSITIVE), Some(BigInt::zero()));
|
||||||
|
assert_eq!(BigInt::from_f32(0.5), Some(BigInt::zero()));
|
||||||
|
assert_eq!(BigInt::from_f32(0.99999), Some(BigInt::zero()));
|
||||||
|
assert_eq!(BigInt::from_f32(f32::consts::E), Some(BigInt::from(2u32)));
|
||||||
|
assert_eq!(BigInt::from_f32(f32::consts::PI), Some(BigInt::from(3u32)));
|
||||||
|
|
||||||
|
// special float values
|
||||||
|
assert_eq!(BigInt::from_f32(f32::NAN), None);
|
||||||
|
assert_eq!(BigInt::from_f32(f32::INFINITY), None);
|
||||||
|
assert_eq!(BigInt::from_f32(f32::NEG_INFINITY), None);
|
||||||
|
|
||||||
|
// largest BigInt that will round to a finite f32 value
|
||||||
|
let big_num = (BigInt::one() << 128) - BigInt::one() - (BigInt::one() << (128 - 25));
|
||||||
|
assert_eq!(big_num.to_f32(), Some(f32::MAX));
|
||||||
|
assert_eq!((&big_num + BigInt::one()).to_f32(), None);
|
||||||
|
assert_eq!((-&big_num).to_f32(), Some(f32::MIN));
|
||||||
|
assert_eq!(((-&big_num) - BigInt::one()).to_f32(), None);
|
||||||
|
|
||||||
|
assert_eq!(((BigInt::one() << 128) - BigInt::one()).to_f32(), None);
|
||||||
|
assert_eq!((BigInt::one() << 128).to_f32(), None);
|
||||||
|
assert_eq!((-((BigInt::one() << 128) - BigInt::one())).to_f32(), None);
|
||||||
|
assert_eq!((-(BigInt::one() << 128)).to_f32(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_convert_f64() {
|
||||||
|
fn check(b1: &BigInt, f: f64) {
|
||||||
|
let b2 = BigInt::from_f64(f).unwrap();
|
||||||
|
assert_eq!(b1, &b2);
|
||||||
|
assert_eq!(b1.to_f64().unwrap(), f);
|
||||||
|
let neg_b1 = -b1;
|
||||||
|
let neg_b2 = BigInt::from_f64(-f).unwrap();
|
||||||
|
assert_eq!(neg_b1, neg_b2);
|
||||||
|
assert_eq!(neg_b1.to_f64().unwrap(), -f);
|
||||||
|
}
|
||||||
|
|
||||||
|
check(&BigInt::zero(), 0.0);
|
||||||
|
check(&BigInt::one(), 1.0);
|
||||||
|
check(&BigInt::from(u32::MAX), 2.0.powi(32) - 1.0);
|
||||||
|
check(&BigInt::from(1u64 << 32), 2.0.powi(32));
|
||||||
|
check(&BigInt::from_slice(Plus, &[0, 0, 1]), 2.0.powi(64));
|
||||||
|
check(&((BigInt::one() << 100) + (BigInt::one() << 152)), 2.0.powi(100) + 2.0.powi(152));
|
||||||
|
check(&(BigInt::one() << 1023), 2.0.powi(1023));
|
||||||
|
check(&(BigInt::from((1u64 << 53) - 1) << (1024 - 53)), f64::MAX);
|
||||||
|
|
||||||
|
// keeping all 53 digits with the bits at different offsets to the BigDigits
|
||||||
|
let x: u64 = 0b0000000000011110111110110111111101110111101111011111011011011101;
|
||||||
|
let mut f = x as f64;
|
||||||
|
let mut b = BigInt::from(x);
|
||||||
|
for _ in 0..128 {
|
||||||
|
check(&b, f);
|
||||||
|
f *= 2.0;
|
||||||
|
b = b << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// test rounding up with the bits at different offsets to the BigDigits
|
||||||
|
let mut f = ((1u64 << 54) - 1) as f64;
|
||||||
|
let mut b = BigInt::from(1u64 << 54);
|
||||||
|
for _ in 0..128 {
|
||||||
|
assert_eq!(b.to_f64(), Some(f));
|
||||||
|
f *= 2.0;
|
||||||
|
b = b << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// rounding
|
||||||
|
assert_eq!(BigInt::from_f64(-f64::consts::PI), Some(BigInt::from(-3i32)));
|
||||||
|
assert_eq!(BigInt::from_f64(-f64::consts::E), Some(BigInt::from(-2i32)));
|
||||||
|
assert_eq!(BigInt::from_f64(-0.99999), Some(BigInt::zero()));
|
||||||
|
assert_eq!(BigInt::from_f64(-0.5), Some(BigInt::zero()));
|
||||||
|
assert_eq!(BigInt::from_f64(-0.0), Some(BigInt::zero()));
|
||||||
|
assert_eq!(BigInt::from_f64(f64::MIN_POSITIVE / 2.0), Some(BigInt::zero()));
|
||||||
|
assert_eq!(BigInt::from_f64(f64::MIN_POSITIVE), Some(BigInt::zero()));
|
||||||
|
assert_eq!(BigInt::from_f64(0.5), Some(BigInt::zero()));
|
||||||
|
assert_eq!(BigInt::from_f64(0.99999), Some(BigInt::zero()));
|
||||||
|
assert_eq!(BigInt::from_f64(f64::consts::E), Some(BigInt::from(2u32)));
|
||||||
|
assert_eq!(BigInt::from_f64(f64::consts::PI), Some(BigInt::from(3u32)));
|
||||||
|
|
||||||
|
// special float values
|
||||||
|
assert_eq!(BigInt::from_f64(f64::NAN), None);
|
||||||
|
assert_eq!(BigInt::from_f64(f64::INFINITY), None);
|
||||||
|
assert_eq!(BigInt::from_f64(f64::NEG_INFINITY), None);
|
||||||
|
|
||||||
|
// largest BigInt that will round to a finite f64 value
|
||||||
|
let big_num = (BigInt::one() << 1024) - BigInt::one() - (BigInt::one() << (1024 - 54));
|
||||||
|
assert_eq!(big_num.to_f64(), Some(f64::MAX));
|
||||||
|
assert_eq!((&big_num + BigInt::one()).to_f64(), None);
|
||||||
|
assert_eq!((-&big_num).to_f64(), Some(f64::MIN));
|
||||||
|
assert_eq!(((-&big_num) - BigInt::one()).to_f64(), None);
|
||||||
|
|
||||||
|
assert_eq!(((BigInt::one() << 1024) - BigInt::one()).to_f64(), None);
|
||||||
|
assert_eq!((BigInt::one() << 1024).to_f64(), None);
|
||||||
|
assert_eq!((-((BigInt::one() << 1024) - BigInt::one())).to_f64(), None);
|
||||||
|
assert_eq!((-(BigInt::one() << 1024)).to_f64(), None);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_convert_to_biguint() {
|
fn test_convert_to_biguint() {
|
||||||
fn check(n: BigInt, ans_1: BigUint) {
|
fn check(n: BigInt, ans_1: BigUint) {
|
||||||
|
|
Loading…
Reference in New Issue