diff options
Diffstat (limited to 'third_party/rust/float-cmp/src/ulps.rs')
-rw-r--r-- | third_party/rust/float-cmp/src/ulps.rs | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/third_party/rust/float-cmp/src/ulps.rs b/third_party/rust/float-cmp/src/ulps.rs new file mode 100644 index 0000000000..51f186f94e --- /dev/null +++ b/third_party/rust/float-cmp/src/ulps.rs @@ -0,0 +1,240 @@ +// Copyright 2014-2018 Optimal Computing (NZ) Ltd. +// Licensed under the MIT license. See LICENSE for details. + +#[cfg(feature="num_traits")] +use num_traits::NumCast; + +/// A trait for floating point numbers which computes the number of representable +/// values or ULPs (Units of Least Precision) that separate the two given values. +#[cfg(feature="num_traits")] +pub trait Ulps { + type U: Copy + NumCast; + + /// The number of representable values or ULPs (Units of Least Precision) that + /// separate `self` and `other`. The result `U` is an integral value, and will + /// be zero if `self` and `other` are exactly equal. + fn ulps(&self, other: &Self) -> <Self as Ulps>::U; + + /// The next representable number above this one + fn next(&self) -> Self; + + /// The previous representable number below this one + fn prev(&self) -> Self; +} + +#[cfg(not(feature="num_traits"))] +pub trait Ulps { + type U: Copy; + + /// The number of representable values or ULPs (Units of Least Precision) that + /// separate `self` and `other`. The result `U` is an integral value, and will + /// be zero if `self` and `other` are exactly equal. + fn ulps(&self, other: &Self) -> <Self as Ulps>::U; + + /// The next representable number above this one + fn next(&self) -> Self; + + /// The previous representable number below this one + fn prev(&self) -> Self; +} + +impl Ulps for f32 { + type U = i32; + + fn ulps(&self, other: &f32) -> i32 { + + // IEEE754 defined floating point storage representation to + // maintain their order when their bit patterns are interpreted as + // integers. This is a huge boon to the task at hand, as we can + // reinterpret them as integers to find out how many ULPs apart any + // two floats are + + // Setup integer representations of the input + let ai32: i32 = self.to_bits() as i32; + let bi32: i32 = other.to_bits() as i32; + + ai32.wrapping_sub(bi32) + } + + fn next(&self) -> Self { + if self.is_infinite() && *self > 0.0 { + *self + } else if *self == -0.0 && self.is_sign_negative() { + 0.0 + } else { + let mut u = self.to_bits(); + if *self >= 0.0 { + u += 1; + } else { + u -= 1; + } + f32::from_bits(u) + } + } + + fn prev(&self) -> Self { + if self.is_infinite() && *self < 0.0 { + *self + } else if *self == 0.0 && self.is_sign_positive() { + -0.0 + } else { + let mut u = self.to_bits(); + if *self <= -0.0 { + u += 1; + } else { + u -= 1; + } + f32::from_bits(u) + } + } +} + +#[test] +fn f32_ulps_test1() { + let x: f32 = 1000000_f32; + let y: f32 = 1000000.1_f32; + println!("DIST IS {}",x.ulps(&y)); + assert!(x.ulps(&y) == -2); +} + +#[test] +fn f32_ulps_test2() { + let pzero: f32 = f32::from_bits(0x00000000_u32); + let nzero: f32 = f32::from_bits(0x80000000_u32); + println!("DIST IS {}",pzero.ulps(&nzero)); + assert!(pzero.ulps(&nzero) == -2147483648); +} +#[test] +fn f32_ulps_test3() { + let pinf: f32 = f32::from_bits(0x7f800000_u32); + let ninf: f32 = f32::from_bits(0xff800000_u32); + println!("DIST IS {}",pinf.ulps(&ninf)); + assert!(pinf.ulps(&ninf) == -2147483648); +} + +#[test] +fn f32_ulps_test4() { + let x: f32 = f32::from_bits(0x63a7f026_u32); + let y: f32 = f32::from_bits(0x63a7f023_u32); + println!("DIST IS {}",x.ulps(&y)); + assert!(x.ulps(&y) == 3); +} + +#[test] +fn f32_ulps_test5() { + let x: f32 = 2.0; + let ulps: i32 = x.to_bits() as i32; + let x2: f32 = <f32>::from_bits(ulps as u32); + assert_eq!(x, x2); +} + +#[test] +fn f32_ulps_test6() { + let negzero: f32 = -0.; + let zero: f32 = 0.; + assert_eq!(negzero.next(), zero); + assert_eq!(zero.prev(), negzero); + assert!(negzero.prev() < 0.0); + assert!(zero.next() > 0.0); +} + +impl Ulps for f64 { + type U = i64; + + fn ulps(&self, other: &f64) -> i64 { + + // IEEE754 defined floating point storage representation to + // maintain their order when their bit patterns are interpreted as + // integers. This is a huge boon to the task at hand, as we can + // reinterpret them as integers to find out how many ULPs apart any + // two floats are + + // Setup integer representations of the input + let ai64: i64 = self.to_bits() as i64; + let bi64: i64 = other.to_bits() as i64; + + ai64.wrapping_sub(bi64) + } + + fn next(&self) -> Self { + if self.is_infinite() && *self > 0.0 { + *self + } else if *self == -0.0 && self.is_sign_negative() { + 0.0 + } else { + let mut u = self.to_bits(); + if *self >= 0.0 { + u += 1; + } else { + u -= 1; + } + f64::from_bits(u) + } + } + + fn prev(&self) -> Self { + if self.is_infinite() && *self < 0.0 { + *self + } else if *self == 0.0 && self.is_sign_positive() { + -0.0 + } else { + let mut u = self.to_bits(); + if *self <= -0.0 { + u += 1; + } else { + u -= 1; + } + f64::from_bits(u) + } + } +} + +#[test] +fn f64_ulps_test1() { + let x: f64 = 1000000_f64; + let y: f64 = 1000000.00000001_f64; + println!("DIST IS {}",x.ulps(&y)); + assert!(x.ulps(&y) == -86); +} + +#[test] +fn f64_ulps_test2() { + let pzero: f64 = f64::from_bits(0x0000000000000000_u64); + let nzero: f64 = f64::from_bits(0x8000000000000000_u64); + println!("DIST IS {}",pzero.ulps(&nzero)); + assert!(pzero.ulps(&nzero) == -9223372036854775808i64); +} +#[test] +fn f64_ulps_test3() { + let pinf: f64 = f64::from_bits(0x7f80000000000000_u64); + let ninf: f64 = f64::from_bits(0xff80000000000000_u64); + println!("DIST IS {}",pinf.ulps(&ninf)); + assert!(pinf.ulps(&ninf) == -9223372036854775808i64); +} + +#[test] +fn f64_ulps_test4() { + let x: f64 = f64::from_bits(0xd017f6cc63a7f026_u64); + let y: f64 = f64::from_bits(0xd017f6cc63a7f023_u64); + println!("DIST IS {}",x.ulps(&y)); + assert!(x.ulps(&y) == 3); +} + +#[test] +fn f64_ulps_test5() { + let x: f64 = 2.0; + let ulps: i64 = x.to_bits() as i64; + let x2: f64 = <f64>::from_bits(ulps as u64); + assert_eq!(x, x2); +} + +#[test] +fn f64_ulps_test6() { + let negzero: f64 = -0.; + let zero: f64 = 0.; + assert_eq!(negzero.next(), zero); + assert_eq!(zero.prev(), negzero); + assert!(negzero.prev() < 0.0); + assert!(zero.next() > 0.0); +} + |