1// Copyright 2014-2020 Optimal Computing (NZ) Ltd.
2// Licensed under the MIT license. See LICENSE for details.
34use super::Ulps;
5#[cfg(feature = "num-traits")]
6#[allow(unused_imports)]
7use num_traits::float::FloatCore;
89/// 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 {
14type Flt: Ulps;
1516/// 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.
21fn approx_eq_ulps(&self, other: &Self, ulps: <Self::Flt as Ulps>::U) -> bool;
2223/// 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]
29fn approx_ne_ulps(&self, other: &Self, ulps: <Self::Flt as Ulps>::U) -> bool {
30 !self.approx_eq_ulps(other, ulps)
31 }
32}
3334impl ApproxEqUlps for f32 {
35type Flt = f32;
3637fn 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.
40if *self == *other {
41return true;
42 }
4344// Handle differing signs as a special case, even if
45 // they are very close, most people consider them
46 // unequal.
47if self.is_sign_positive() != other.is_sign_positive() {
48return false;
49 }
5051let diff: i32 = self.ulps(other);
52 diff >= -ulps && diff <= ulps
53 }
54}
5556#[test]
57fn f32_approx_eq_ulps_test1() {
58let f: f32 = 0.1_f32;
59let mut sum: f32 = 0.0_f32;
60for _ in 0_isize..10_isize {
61 sum += f;
62 }
63let product: f32 = f * 10.0_f32;
64assert!(sum != product); // Should not be directly equal:
65assert!(sum.approx_eq_ulps(&product, 1) == true); // But should be close
66assert!(sum.approx_eq_ulps(&product, 0) == false);
67}
68#[test]
69fn f32_approx_eq_ulps_test2() {
70let x: f32 = 1000000_f32;
71let y: f32 = 1000000.1_f32;
72assert!(x != y); // Should not be directly equal
73assert!(x.approx_eq_ulps(&y, 2) == true);
74assert!(x.approx_eq_ulps(&y, 1) == false);
75}
76#[test]
77fn f32_approx_eq_ulps_test_zeroes() {
78let x: f32 = 0.0_f32;
79let y: f32 = -0.0_f32;
80assert!(x.approx_eq_ulps(&y, 0) == true);
81}
8283impl ApproxEqUlps for f64 {
84type Flt = f64;
8586fn approx_eq_ulps(&self, other: &f64, ulps: i64) -> bool {
87// -0 and +0 are drastically far in ulps terms, so
88 // we need a special case for that.
89if *self == *other {
90return true;
91 }
9293// Handle differing signs as a special case, even if
94 // they are very close, most people consider them
95 // unequal.
96if self.is_sign_positive() != other.is_sign_positive() {
97return false;
98 }
99100let diff: i64 = self.ulps(other);
101 diff >= -ulps && diff <= ulps
102 }
103}
104105#[test]
106fn f64_approx_eq_ulps_test1() {
107let f: f64 = 0.1_f64;
108let mut sum: f64 = 0.0_f64;
109for _ in 0_isize..10_isize {
110 sum += f;
111 }
112let product: f64 = f * 10.0_f64;
113assert!(sum != product); // Should not be directly equal:
114assert!(sum.approx_eq_ulps(&product, 1) == true); // But should be close
115assert!(sum.approx_eq_ulps(&product, 0) == false);
116}
117#[test]
118fn f64_approx_eq_ulps_test2() {
119let x: f64 = 1000000_f64;
120let y: f64 = 1000000.0000000003_f64;
121assert!(x != y); // Should not be directly equal
122assert!(x.approx_eq_ulps(&y, 3) == true);
123assert!(x.approx_eq_ulps(&y, 2) == false);
124}
125#[test]
126fn f64_approx_eq_ulps_test_zeroes() {
127let x: f64 = 0.0_f64;
128let y: f64 = -0.0_f64;
129assert!(x.approx_eq_ulps(&y, 0) == true);
130}