approx/
relative_eq.rs

1use core::{cell, f32, f64};
2#[cfg(feature = "num-complex")]
3use num_complex::Complex;
4use AbsDiffEq;
5
6/// Equality comparisons between two numbers using both the absolute difference and
7/// relative based comparisons.
8pub trait RelativeEq<Rhs = Self>: AbsDiffEq<Rhs>
9where
10    Rhs: ?Sized,
11{
12    /// The default relative tolerance for testing values that are far-apart.
13    ///
14    /// This is used when no `max_relative` value is supplied to the [`relative_eq`] macro.
15    fn default_max_relative() -> Self::Epsilon;
16
17    /// A test for equality that uses a relative comparison if the values are far apart.
18    fn relative_eq(&self, other: &Rhs, epsilon: Self::Epsilon, max_relative: Self::Epsilon)
19        -> bool;
20
21    /// The inverse of [`RelativeEq::relative_eq`].
22    fn relative_ne(
23        &self,
24        other: &Rhs,
25        epsilon: Self::Epsilon,
26        max_relative: Self::Epsilon,
27    ) -> bool {
28        !Self::relative_eq(self, other, epsilon, max_relative)
29    }
30}
31
32///////////////////////////////////////////////////////////////////////////////////////////////////
33// Base implementations
34///////////////////////////////////////////////////////////////////////////////////////////////////
35
36// Implementation based on: [Comparing Floating Point Numbers, 2012 Edition]
37// (https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/)
38macro_rules! impl_relative_eq {
39    ($T:ident, $U:ident) => {
40        impl RelativeEq for $T {
41            #[inline]
42            fn default_max_relative() -> $T {
43                $T::EPSILON
44            }
45
46            #[inline]
47            #[allow(unused_imports)]
48            fn relative_eq(&self, other: &$T, epsilon: $T, max_relative: $T) -> bool {
49                use num_traits::float::FloatCore;
50                // Handle same infinities
51                if self == other {
52                    return true;
53                }
54
55                // Handle remaining infinities
56                if $T::is_infinite(*self) || $T::is_infinite(*other) {
57                    return false;
58                }
59
60                let abs_diff = $T::abs(self - other);
61
62                // For when the numbers are really close together
63                if abs_diff <= epsilon {
64                    return true;
65                }
66
67                let abs_self = $T::abs(*self);
68                let abs_other = $T::abs(*other);
69
70                let largest = if abs_other > abs_self {
71                    abs_other
72                } else {
73                    abs_self
74                };
75
76                // Use a relative difference comparison
77                abs_diff <= largest * max_relative
78            }
79        }
80    };
81}
82
83impl_relative_eq!(f32, i32);
84impl_relative_eq!(f64, i64);
85
86///////////////////////////////////////////////////////////////////////////////////////////////////
87// Derived implementations
88///////////////////////////////////////////////////////////////////////////////////////////////////
89
90impl<'a, T: RelativeEq + ?Sized> RelativeEq for &'a T {
91    #[inline]
92    fn default_max_relative() -> T::Epsilon {
93        T::default_max_relative()
94    }
95
96    #[inline]
97    fn relative_eq(&self, other: &&'a T, epsilon: T::Epsilon, max_relative: T::Epsilon) -> bool {
98        T::relative_eq(*self, *other, epsilon, max_relative)
99    }
100}
101
102impl<'a, T: RelativeEq + ?Sized> RelativeEq for &'a mut T {
103    #[inline]
104    fn default_max_relative() -> T::Epsilon {
105        T::default_max_relative()
106    }
107
108    #[inline]
109    fn relative_eq(
110        &self,
111        other: &&'a mut T,
112        epsilon: T::Epsilon,
113        max_relative: T::Epsilon,
114    ) -> bool {
115        T::relative_eq(*self, *other, epsilon, max_relative)
116    }
117}
118
119impl<T: RelativeEq + Copy> RelativeEq for cell::Cell<T> {
120    #[inline]
121    fn default_max_relative() -> T::Epsilon {
122        T::default_max_relative()
123    }
124
125    #[inline]
126    fn relative_eq(
127        &self,
128        other: &cell::Cell<T>,
129        epsilon: T::Epsilon,
130        max_relative: T::Epsilon,
131    ) -> bool {
132        T::relative_eq(&self.get(), &other.get(), epsilon, max_relative)
133    }
134}
135
136impl<T: RelativeEq + ?Sized> RelativeEq for cell::RefCell<T> {
137    #[inline]
138    fn default_max_relative() -> T::Epsilon {
139        T::default_max_relative()
140    }
141
142    #[inline]
143    fn relative_eq(
144        &self,
145        other: &cell::RefCell<T>,
146        epsilon: T::Epsilon,
147        max_relative: T::Epsilon,
148    ) -> bool {
149        T::relative_eq(&self.borrow(), &other.borrow(), epsilon, max_relative)
150    }
151}
152
153impl<A, B> RelativeEq<[B]> for [A]
154where
155    A: RelativeEq<B>,
156    A::Epsilon: Clone,
157{
158    #[inline]
159    fn default_max_relative() -> A::Epsilon {
160        A::default_max_relative()
161    }
162
163    #[inline]
164    fn relative_eq(&self, other: &[B], epsilon: A::Epsilon, max_relative: A::Epsilon) -> bool {
165        self.len() == other.len()
166            && Iterator::zip(self.iter(), other)
167                .all(|(x, y)| A::relative_eq(x, y, epsilon.clone(), max_relative.clone()))
168    }
169}
170
171#[cfg(feature = "num-complex")]
172impl<T: RelativeEq> RelativeEq for Complex<T>
173where
174    T::Epsilon: Clone,
175{
176    #[inline]
177    fn default_max_relative() -> T::Epsilon {
178        T::default_max_relative()
179    }
180
181    #[inline]
182    fn relative_eq(
183        &self,
184        other: &Complex<T>,
185        epsilon: T::Epsilon,
186        max_relative: T::Epsilon,
187    ) -> bool {
188        T::relative_eq(&self.re, &other.re, epsilon.clone(), max_relative.clone())
189            && T::relative_eq(&self.im, &other.im, epsilon, max_relative)
190    }
191}