// Copyright 2014-2018 Optimal Computing (NZ) Ltd. // Licensed under the MIT license. See LICENSE for details.
use std::{f32,f64}; usesuper::Ulps;
/// ApproxEq is a trait for approximate equality comparisons. pubtrait ApproxEq: Sized { /// The Margin type defines a margin within which two values are to be /// considered approximately equal. It must implement Default so that /// approx_eq() can be called on unknown types. type Margin: Copy + Default;
/// This method tests for `self` and `other` values to be approximately equal /// within `margin`. fn approx_eq<M: Into<Self::Margin>>(self, other: Self, margin: M) -> bool;
/// This method tests for `self` and `other` values to be not approximately /// equal within `margin`. fn approx_ne<M: Into<Self::Margin>>(self, other: Self, margin: M) -> bool {
!self.approx_eq(other, margin)
}
}
/// This type defines a margin within two f32s might be considered equal /// and is intended as the associated type for the `ApproxEq` trait. /// /// Two methods are used to determine approximate equality. /// /// First an epsilon method is used, considering them approximately equal if they /// differ by <= `epsilon`. This will only succeed for very small numbers. /// Note that it may succeed even if the parameters are of differing signs straddling /// zero. /// /// The second method considers how many ULPs (units of least precision, units in /// the last place, which is the integer number of floating point representations /// that the parameters are separated by) different the parameters are and considers /// them approximately equal if this is <= `ulps`. For large floating point numbers, /// an ULP can be a rather large gap, but this kind of comparison is necessary /// because floating point operations must round to the nearest representable value /// and so larger floating point values accumulate larger errors. #[repr(C)] #[derive(Debug, Clone, Copy)] pubstruct F32Margin { pub epsilon: f32, pub ulps: i32
} impl Default for F32Margin { #[inline] fn default() -> F32Margin {
F32Margin {
epsilon: f32::EPSILON,
ulps: 4
}
}
} impl F32Margin { #[inline] pubfn zero() -> F32Margin {
F32Margin {
epsilon: 0.0,
ulps: 0
}
} pubfn epsilon(self, epsilon: f32) -> Self {
F32Margin {
epsilon: epsilon,
..self
}
} pubfn ulps(self, ulps: i32) -> Self {
F32Margin {
ulps: ulps,
..self
}
}
} impl From<(f32, i32)> for F32Margin { fn from(m: (f32, i32)) -> F32Margin {
F32Margin {
epsilon: m.0,
ulps: m.1
}
}
}
impl ApproxEq for f32 { type Margin = F32Margin;
fn approx_eq<M: Into<Self::Margin>>(self, other: f32, margin: M) -> bool { let margin = margin.into();
// Check for exact equality first. This is often true, and so we get the // performance benefit of only doing one compare in most cases. self==other ||
{ // Perform ulps comparion last let diff: i32 = self.ulps(&other);
saturating_abs_i32!(diff) <= margin.ulps
}
}
}
#[test] fn f32_approx_eq_test1() { let f: f32 = 0.0_f32; let g: f32 = -0.0000000000000005551115123125783_f32;
assert!(f != g); // Should not be directly equal
assert!(f.approx_eq(g, (f32::EPSILON, 0)) == true);
} #[test] fn f32_approx_eq_test2() { let f: f32 = 0.0_f32; let g: f32 = -0.0_f32;
assert!(f.approx_eq(g, (f32::EPSILON, 0)) == true);
} #[test] fn f32_approx_eq_test3() { let f: f32 = 0.0_f32; let g: f32 = 0.00000000000000001_f32;
assert!(f.approx_eq(g, (f32::EPSILON, 0)) == true);
} #[test] fn f32_approx_eq_test4() { let f: f32 = 0.00001_f32; let g: f32 = 0.00000000000000001_f32;
assert!(f.approx_eq(g, (f32::EPSILON, 0)) == false);
} #[test] fn f32_approx_eq_test5() { let f: f32 = 0.1_f32; letmut sum: f32 = 0.0_f32; for _ in0_isize..10_isize { sum += f; } let product: f32 = f * 10.0_f32;
assert!(sum != product); // Should not be directly equal:
println!("Ulps Difference: {}",sum.ulps(&product));
assert!(sum.approx_eq(product, (f32::EPSILON, 1)) == true);
assert!(sum.approx_eq(product, F32Margin::zero()) == false);
} #[test] fn f32_approx_eq_test6() { let x: f32 = 1000000_f32; let y: f32 = 1000000.1_f32;
assert!(x != y); // Should not be directly equal
assert!(x.approx_eq(y, (0.0, 2)) == true); // 2 ulps does it // epsilon method no good here:
assert!(x.approx_eq(y, (1000.0 * f32::EPSILON, 0)) == false);
}
/// This type defines a margin within two f32s might be considered equal /// and is intended as the associated type for the `ApproxEq` trait. /// /// Two methods are used to determine approximate equality. /// /// First an epsilon method is used, considering them approximately equal if they /// differ by <= `epsilon`. This will only succeed for very small numbers. /// Note that it may succeed even if the parameters are of differing signs straddling /// zero. /// /// The second method considers how many ULPs (units of least precision, units in /// the last place, which is the integer number of floating point representations /// that the parameters are separated by) different the parameters are and considers /// them approximately equal if this <= `ulps`. For large floating point numbers, /// an ULP can be a rather large gap, but this kind of comparison is necessary /// because floating point operations must round to the nearest representable value /// and so larger floating point values accumulate larger errors. #[derive(Debug, Clone, Copy)] pubstruct F64Margin { pub epsilon: f64, pub ulps: i64
} impl Default for F64Margin { #[inline] fn default() -> F64Margin {
F64Margin {
epsilon: f64::EPSILON,
ulps: 4
}
}
} impl F64Margin { #[inline] pubfn zero() -> F64Margin {
F64Margin {
epsilon: 0.0,
ulps: 0
}
} pubfn epsilon(self, epsilon: f64) -> Self {
F64Margin {
epsilon: epsilon,
..self
}
} pubfn ulps(self, ulps: i64) -> Self {
F64Margin {
ulps: ulps,
..self
}
}
} impl From<(f64, i64)> for F64Margin { fn from(m: (f64, i64)) -> F64Margin {
F64Margin {
epsilon: m.0,
ulps: m.1
}
}
}
impl ApproxEq for f64 { type Margin = F64Margin;
fn approx_eq<M: Into<Self::Margin>>(self, other: f64, margin: M) -> bool { let margin = margin.into();
// Check for exact equality first. This is often true, and so we get the // performance benefit of only doing one compare in most cases. self==other ||
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.