float_cmp/
ulps_eq.rs

1// Copyright 2014-2020 Optimal Computing (NZ) Ltd.
2// Licensed under the MIT license.  See LICENSE for details.
3
4#[cfg(feature = "num-traits")]
5#[allow(unused_imports)]
6use num_traits::float::FloatCore;
7use super::Ulps;
8
9/// ApproxEqUlps is a trait for approximate equality comparisons.
10/// The associated type Flt is a floating point type which implements Ulps, and is
11/// required so that this trait can be implemented for compound types (e.g. vectors),
12/// not just for the floats themselves.
13pub trait ApproxEqUlps {
14    type Flt: Ulps;
15
16    /// This method tests for `self` and `other` values to be approximately equal
17    /// within ULPs (Units of Least Precision) floating point representations.
18    /// Differing signs are always unequal with this method, and zeroes are only
19    /// equal to zeroes. Use approx_eq() from the ApproxEq trait if that is more
20    /// appropriate.
21    fn approx_eq_ulps(&self, other: &Self, ulps: <Self::Flt as Ulps>::U) -> bool;
22
23    /// This method tests for `self` and `other` values to be not approximately
24    /// equal within ULPs (Units of Least Precision) floating point representations.
25    /// Differing signs are always unequal with this method, and zeroes are only
26    /// equal to zeroes. Use approx_eq() from the ApproxEq trait if that is more
27    /// appropriate.
28    #[inline]
29    fn approx_ne_ulps(&self, other: &Self, ulps: <Self::Flt as Ulps>::U) -> bool {
30        !self.approx_eq_ulps(other, ulps)
31    }
32}
33
34impl ApproxEqUlps for f32 {
35    type Flt = f32;
36
37    fn approx_eq_ulps(&self, other: &f32, ulps: i32) -> bool {
38        // -0 and +0 are drastically far in ulps terms, so
39        // we need a special case for that.
40        if *self==*other { return true; }
41
42        // Handle differing signs as a special case, even if
43        // they are very close, most people consider them
44        // unequal.
45        if self.is_sign_positive() != other.is_sign_positive() { return false; }
46
47        let diff: i32 = self.ulps(other);
48        diff >= -ulps && diff <= ulps
49    }
50}
51
52#[test]
53fn f32_approx_eq_ulps_test1() {
54    let f: f32 = 0.1_f32;
55    let mut sum: f32 = 0.0_f32;
56    for _ in 0_isize..10_isize { sum += f; }
57    let product: f32 = f * 10.0_f32;
58    assert!(sum != product); // Should not be directly equal:
59    assert!(sum.approx_eq_ulps(&product,1) == true); // But should be close
60    assert!(sum.approx_eq_ulps(&product,0) == false);
61}
62#[test]
63fn f32_approx_eq_ulps_test2() {
64    let x: f32 = 1000000_f32;
65    let y: f32 = 1000000.1_f32;
66    assert!(x != y); // Should not be directly equal
67    assert!(x.approx_eq_ulps(&y,2) == true);
68    assert!(x.approx_eq_ulps(&y,1) == false);
69}
70#[test]
71fn f32_approx_eq_ulps_test_zeroes() {
72    let x: f32 = 0.0_f32;
73    let y: f32 = -0.0_f32;
74    assert!(x.approx_eq_ulps(&y,0) == true);
75}
76
77impl ApproxEqUlps for f64 {
78    type Flt = f64;
79
80    fn approx_eq_ulps(&self, other: &f64, ulps: i64) -> bool {
81        // -0 and +0 are drastically far in ulps terms, so
82        // we need a special case for that.
83        if *self==*other { return true; }
84
85        // Handle differing signs as a special case, even if
86        // they are very close, most people consider them
87        // unequal.
88        if self.is_sign_positive() != other.is_sign_positive() { return false; }
89
90        let diff: i64 = self.ulps(other);
91        diff >= -ulps && diff <= ulps
92    }
93}
94
95#[test]
96fn f64_approx_eq_ulps_test1() {
97    let f: f64 = 0.1_f64;
98    let mut sum: f64 = 0.0_f64;
99    for _ in 0_isize..10_isize { sum += f; }
100    let product: f64 = f * 10.0_f64;
101    assert!(sum != product); // Should not be directly equal:
102    assert!(sum.approx_eq_ulps(&product,1) == true); // But should be close
103    assert!(sum.approx_eq_ulps(&product,0) == false);
104}
105#[test]
106fn f64_approx_eq_ulps_test2() {
107    let x: f64 = 1000000_f64;
108    let y: f64 = 1000000.0000000003_f64;
109    assert!(x != y); // Should not be directly equal
110    assert!(x.approx_eq_ulps(&y,3) == true);
111    assert!(x.approx_eq_ulps(&y,2) == false);
112}
113#[test]
114fn f64_approx_eq_ulps_test_zeroes() {
115    let x: f64 = 0.0_f64;
116    let y: f64 = -0.0_f64;
117    assert!(x.approx_eq_ulps(&y,0) == true);
118}