Merge branch 'master' into missing-impls-for-wrapping

This commit is contained in:
Josh Stone 2017-05-06 21:25:28 -07:00 committed by GitHub
commit 9115df6179
2 changed files with 208 additions and 2 deletions

View File

@ -3,7 +3,7 @@
A collection of numeric types and traits for Rust. A collection of numeric types and traits for Rust.
This includes new types for big integers, rationals, and complex numbers, This includes new types for big integers, rationals, and complex numbers,
new traits for generic programming on numeric properties like `Integer, new traits for generic programming on numeric properties like `Integer`,
and generic range iterators. and generic range iterators.
[Documentation](http://rust-num.github.io/num) [Documentation](http://rust-num.github.io/num)

View File

@ -36,7 +36,7 @@ use std::str::FromStr;
use bigint::{BigInt, BigUint, Sign}; use bigint::{BigInt, BigUint, Sign};
use integer::Integer; use integer::Integer;
use traits::{FromPrimitive, Float, PrimInt, Num, Signed, Zero, One}; use traits::{FromPrimitive, Float, PrimInt, Num, Signed, Zero, One, Bounded, NumCast};
/// Represents the ratio between 2 numbers. /// Represents the ratio between 2 numbers.
#[derive(Copy, Clone, Hash, Debug)] #[derive(Copy, Clone, Hash, Debug)]
@ -668,6 +668,179 @@ impl RatioErrorKind {
} }
} }
impl FromPrimitive for Ratio<BigInt> {
fn from_i64(n: i64) -> Option<Self> {
Some(Ratio::from_integer(n.into()))
}
fn from_u64(n: u64) -> Option<Self> {
Some(Ratio::from_integer(n.into()))
}
fn from_f32(n: f32) -> Option<Self> {
Ratio::from_float(n)
}
fn from_f64(n: f64) -> Option<Self> {
Ratio::from_float(n)
}
}
macro_rules! from_primitive_integer {
($typ:ty, $approx:ident) => {
impl FromPrimitive for Ratio<$typ> {
fn from_i64(n: i64) -> Option<Self> {
<$typ as FromPrimitive>::from_i64(n).map(Ratio::from_integer)
}
fn from_u64(n: u64) -> Option<Self> {
<$typ as FromPrimitive>::from_u64(n).map(Ratio::from_integer)
}
fn from_f32(n: f32) -> Option<Self> {
$approx(n, 10e-20, 30)
}
fn from_f64(n: f64) -> Option<Self> {
$approx(n, 10e-20, 30)
}
}
}
}
from_primitive_integer!(i8, approximate_float);
from_primitive_integer!(i16, approximate_float);
from_primitive_integer!(i32, approximate_float);
from_primitive_integer!(i64, approximate_float);
from_primitive_integer!(isize, approximate_float);
from_primitive_integer!(u8, approximate_float_unsigned);
from_primitive_integer!(u16, approximate_float_unsigned);
from_primitive_integer!(u32, approximate_float_unsigned);
from_primitive_integer!(u64, approximate_float_unsigned);
from_primitive_integer!(usize, approximate_float_unsigned);
impl<T: Integer + Signed + Bounded + NumCast + Clone> Ratio<T> {
pub fn approximate_float<F: Float + NumCast>(f: F) -> Option<Ratio<T>> {
// 1/10e-20 < 1/2**32 which seems like a good default, and 30 seems
// to work well. Might want to choose something based on the types in the future, e.g.
// T::max().recip() and T::bits() or something similar.
let epsilon = <F as NumCast>::from(10e-20).expect("Can't convert 10e-20");
approximate_float(f, epsilon, 30)
}
}
fn approximate_float<T, F>(val: F, max_error: F, max_iterations: usize) -> Option<Ratio<T>>
where T: Integer + Signed + Bounded + NumCast + Clone,
F: Float + NumCast
{
let negative = val.is_sign_negative();
let abs_val = val.abs();
let r = approximate_float_unsigned(abs_val, max_error, max_iterations);
// Make negative again if needed
if negative {
r.map(|r| r.neg())
} else {
r
}
}
// No Unsigned constraint because this also works on positive integers and is called
// like that, see above
fn approximate_float_unsigned<T, F>(val: F, max_error: F, max_iterations: usize) -> Option<Ratio<T>>
where T: Integer + Bounded + NumCast + Clone,
F: Float + NumCast
{
// Continued fractions algorithm
// http://mathforum.org/dr.math/faq/faq.fractions.html#decfrac
if val < F::zero() {
return None;
}
let mut q = val;
let mut n0 = T::zero();
let mut d0 = T::one();
let mut n1 = T::one();
let mut d1 = T::zero();
let t_max = T::max_value();
let t_max_f = match <F as NumCast>::from(t_max.clone()) {
None => return None,
Some(t_max_f) => t_max_f,
};
// 1/epsilon > T::MAX
let epsilon = t_max_f.recip();
// Overflow
if q > t_max_f {
return None;
}
for _ in 0..max_iterations {
let a = match <T as NumCast>::from(q) {
None => break,
Some(a) => a,
};
let a_f = match <F as NumCast>::from(a.clone()) {
None => break,
Some(a_f) => a_f,
};
let f = q - a_f;
// Prevent overflow
if !a.is_zero() &&
(n1 > t_max.clone() / a.clone() ||
d1 > t_max.clone() / a.clone() ||
a.clone() * n1.clone() > t_max.clone() - n0.clone() ||
a.clone() * d1.clone() > t_max.clone() - d0.clone()) {
break;
}
let n = a.clone() * n1.clone() + n0.clone();
let d = a.clone() * d1.clone() + d0.clone();
n0 = n1;
d0 = d1;
n1 = n.clone();
d1 = d.clone();
// Simplify fraction. Doing so here instead of at the end
// allows us to get closer to the target value without overflows
let g = Integer::gcd(&n1, &d1);
if !g.is_zero() {
n1 = n1 / g.clone();
d1 = d1 / g.clone();
}
// Close enough?
let (n_f, d_f) = match (<F as NumCast>::from(n), <F as NumCast>::from(d)) {
(Some(n_f), Some(d_f)) => (n_f, d_f),
_ => break,
};
if (n_f / d_f - val).abs() < max_error {
break;
}
// Prevent division by ~0
if f < epsilon {
break;
}
q = f.recip();
}
// Overflow
if d1.is_zero() {
return None;
}
Some(Ratio::new(n1, d1))
}
#[cfg(test)] #[cfg(test)]
fn hash<T: hash::Hash>(x: &T) -> u64 { fn hash<T: hash::Hash>(x: &T) -> u64 {
use std::hash::Hasher; use std::hash::Hasher;
@ -684,6 +857,7 @@ mod test {
use std::str::FromStr; use std::str::FromStr;
use std::i32; use std::i32;
use std::f64;
use traits::{Zero, One, Signed, FromPrimitive, Float}; use traits::{Zero, One, Signed, FromPrimitive, Float};
pub const _0: Rational = Ratio { pub const _0: Rational = Ratio {
@ -774,6 +948,38 @@ mod test {
let _a = Ratio::new(1, 0); let _a = Ratio::new(1, 0);
} }
#[test]
fn test_approximate_float() {
assert_eq!(Ratio::from_f32(0.5f32), Some(Ratio::new(1i64, 2)));
assert_eq!(Ratio::from_f64(0.5f64), Some(Ratio::new(1i32, 2)));
assert_eq!(Ratio::from_f32(5f32), Some(Ratio::new(5i64, 1)));
assert_eq!(Ratio::from_f64(5f64), Some(Ratio::new(5i32, 1)));
assert_eq!(Ratio::from_f32(29.97f32), Some(Ratio::new(2997i64, 100)));
assert_eq!(Ratio::from_f32(-29.97f32), Some(Ratio::new(-2997i64, 100)));
assert_eq!(Ratio::<i8>::from_f32(63.5f32), Some(Ratio::new(127i8, 2)));
assert_eq!(Ratio::<i8>::from_f32(126.5f32), Some(Ratio::new(126i8, 1)));
assert_eq!(Ratio::<i8>::from_f32(127.0f32), Some(Ratio::new(127i8, 1)));
assert_eq!(Ratio::<i8>::from_f32(127.5f32), None);
assert_eq!(Ratio::<i8>::from_f32(-63.5f32), Some(Ratio::new(-127i8, 2)));
assert_eq!(Ratio::<i8>::from_f32(-126.5f32), Some(Ratio::new(-126i8, 1)));
assert_eq!(Ratio::<i8>::from_f32(-127.0f32), Some(Ratio::new(-127i8, 1)));
assert_eq!(Ratio::<i8>::from_f32(-127.5f32), None);
assert_eq!(Ratio::<u8>::from_f32(-127f32), None);
assert_eq!(Ratio::<u8>::from_f32(127f32), Some(Ratio::new(127u8, 1)));
assert_eq!(Ratio::<u8>::from_f32(127.5f32), Some(Ratio::new(255u8, 2)));
assert_eq!(Ratio::<u8>::from_f32(256f32), None);
assert_eq!(Ratio::<i64>::from_f64(-10e200), None);
assert_eq!(Ratio::<i64>::from_f64(10e200), None);
assert_eq!(Ratio::<i64>::from_f64(f64::INFINITY), None);
assert_eq!(Ratio::<i64>::from_f64(f64::NEG_INFINITY), None);
assert_eq!(Ratio::<i64>::from_f64(f64::NAN), None);
assert_eq!(Ratio::<i64>::from_f64(f64::EPSILON), Some(Ratio::new(1, 4503599627370496)));
assert_eq!(Ratio::<i64>::from_f64(0.0), Some(Ratio::new(0, 1)));
assert_eq!(Ratio::<i64>::from_f64(-0.0), Some(Ratio::new(0, 1)));
}
#[test] #[test]
fn test_cmp() { fn test_cmp() {