approx/lib.rs
1// Copyright 2015 Brendan Zabarauskas
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! A crate that provides facilities for testing the approximate equality of floating-point
16//! based types, using either relative difference, or units in the last place (ULPs)
17//! comparisons.
18//!
19//! You can also use the `*_{eq, ne}!` and `assert_*_{eq, ne}!` macros to test for equality using a
20//! more positional style:
21//!
22//! ```rust
23//! #[macro_use]
24//! extern crate approx;
25//!
26//! use std::f64;
27//!
28//! # fn main() {
29//! abs_diff_eq!(1.0, 1.0);
30//! abs_diff_eq!(1.0, 1.0, epsilon = f64::EPSILON);
31//!
32//! relative_eq!(1.0, 1.0);
33//! relative_eq!(1.0, 1.0, epsilon = f64::EPSILON);
34//! relative_eq!(1.0, 1.0, max_relative = 1.0);
35//! relative_eq!(1.0, 1.0, epsilon = f64::EPSILON, max_relative = 1.0);
36//! relative_eq!(1.0, 1.0, max_relative = 1.0, epsilon = f64::EPSILON);
37//!
38//! ulps_eq!(1.0, 1.0);
39//! ulps_eq!(1.0, 1.0, epsilon = f64::EPSILON);
40//! ulps_eq!(1.0, 1.0, max_ulps = 4);
41//! ulps_eq!(1.0, 1.0, epsilon = f64::EPSILON, max_ulps = 4);
42//! ulps_eq!(1.0, 1.0, max_ulps = 4, epsilon = f64::EPSILON);
43//! # }
44//! ```
45//!
46//! # Implementing approximate equality for custom types
47//!
48//! The `*Eq` traits allow approximate equalities to be implemented on types, based on the
49//! fundamental floating point implementations.
50//!
51//! For example, we might want to be able to do approximate assertions on a complex number type:
52//!
53//! ```rust
54//! #[macro_use]
55//! extern crate approx;
56//! # use approx::{AbsDiffEq, RelativeEq, UlpsEq};
57//!
58//! #[derive(Debug, PartialEq)]
59//! struct Complex<T> {
60//! x: T,
61//! i: T,
62//! }
63//! # impl<T: AbsDiffEq> AbsDiffEq for Complex<T> where T::Epsilon: Copy {
64//! # type Epsilon = T::Epsilon;
65//! # fn default_epsilon() -> T::Epsilon { T::default_epsilon() }
66//! # fn abs_diff_eq(&self, other: &Self, epsilon: T::Epsilon) -> bool {
67//! # T::abs_diff_eq(&self.x, &other.x, epsilon) &&
68//! # T::abs_diff_eq(&self.i, &other.i, epsilon)
69//! # }
70//! # }
71//! # impl<T: RelativeEq> RelativeEq for Complex<T> where T::Epsilon: Copy {
72//! # fn default_max_relative() -> T::Epsilon { T::default_max_relative() }
73//! # fn relative_eq(&self, other: &Self, epsilon: T::Epsilon, max_relative: T::Epsilon)
74//! # -> bool {
75//! # T::relative_eq(&self.x, &other.x, epsilon, max_relative) &&
76//! # T::relative_eq(&self.i, &other.i, epsilon, max_relative)
77//! # }
78//! # }
79//! # impl<T: UlpsEq> UlpsEq for Complex<T> where T::Epsilon: Copy {
80//! # fn default_max_ulps() -> u32 { T::default_max_ulps() }
81//! # fn ulps_eq(&self, other: &Self, epsilon: T::Epsilon, max_ulps: u32) -> bool {
82//! # T::ulps_eq(&self.x, &other.x, epsilon, max_ulps) &&
83//! # T::ulps_eq(&self.i, &other.i, epsilon, max_ulps)
84//! # }
85//! # }
86//!
87//! # fn main() {
88//! let x = Complex { x: 1.2, i: 2.3 };
89//!
90//! assert_relative_eq!(x, x);
91//! assert_ulps_eq!(x, x, max_ulps = 4);
92//! # }
93//! ```
94//!
95//! To do this we can implement [`AbsDiffEq`], [`RelativeEq`] and [`UlpsEq`] generically in terms
96//! of a type parameter that also implements `AbsDiffEq`, `RelativeEq` and `UlpsEq` respectively.
97//! This means that we can make comparisons for either `Complex<f32>` or `Complex<f64>`:
98//!
99//! ```rust
100//! # use approx::{AbsDiffEq, RelativeEq, UlpsEq};
101//! # #[derive(Debug, PartialEq)]
102//! # struct Complex<T> { x: T, i: T, }
103//! #
104//! impl<T: AbsDiffEq> AbsDiffEq for Complex<T> where
105//! T::Epsilon: Copy,
106//! {
107//! type Epsilon = T::Epsilon;
108//!
109//! fn default_epsilon() -> T::Epsilon {
110//! T::default_epsilon()
111//! }
112//!
113//! fn abs_diff_eq(&self, other: &Self, epsilon: T::Epsilon) -> bool {
114//! T::abs_diff_eq(&self.x, &other.x, epsilon) &&
115//! T::abs_diff_eq(&self.i, &other.i, epsilon)
116//! }
117//! }
118//!
119//! impl<T: RelativeEq> RelativeEq for Complex<T> where
120//! T::Epsilon: Copy,
121//! {
122//! fn default_max_relative() -> T::Epsilon {
123//! T::default_max_relative()
124//! }
125//!
126//! fn relative_eq(&self, other: &Self, epsilon: T::Epsilon, max_relative: T::Epsilon) -> bool {
127//! T::relative_eq(&self.x, &other.x, epsilon, max_relative) &&
128//! T::relative_eq(&self.i, &other.i, epsilon, max_relative)
129//! }
130//! }
131//!
132//! impl<T: UlpsEq> UlpsEq for Complex<T> where
133//! T::Epsilon: Copy,
134//! {
135//! fn default_max_ulps() -> u32 {
136//! T::default_max_ulps()
137//! }
138//!
139//! fn ulps_eq(&self, other: &Self, epsilon: T::Epsilon, max_ulps: u32) -> bool {
140//! T::ulps_eq(&self.x, &other.x, epsilon, max_ulps) &&
141//! T::ulps_eq(&self.i, &other.i, epsilon, max_ulps)
142//! }
143//! }
144//! ```
145//!
146//! # References
147//!
148//! Floating point is hard! Thanks goes to these links for helping to make things a _little_
149//! easier to understand:
150//!
151//! - [Comparing Floating Point Numbers, 2012 Edition](
152//! https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/)
153//! - [The Floating Point Guide - Comparison](http://floating-point-gui.de/errors/comparison/)
154//! - [What Every Computer Scientist Should Know About Floating-Point Arithmetic](
155//! https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html)
156
157#![no_std]
158#![allow(clippy::transmute_float_to_int)]
159
160#[cfg(feature = "num-complex")]
161extern crate num_complex;
162extern crate num_traits;
163
164mod abs_diff_eq;
165mod relative_eq;
166mod ulps_eq;
167
168mod macros;
169
170pub use abs_diff_eq::AbsDiffEq;
171pub use relative_eq::RelativeEq;
172pub use ulps_eq::UlpsEq;
173
174/// The requisite parameters for testing for approximate equality using a
175/// absolute difference based comparison.
176///
177/// This is not normally used directly, rather via the
178/// `assert_abs_diff_{eq|ne}!` and `abs_diff_{eq|ne}!` macros.
179///
180/// # Example
181///
182/// ```rust
183/// use std::f64;
184/// use approx::AbsDiff;
185///
186/// AbsDiff::default().eq(&1.0, &1.0);
187/// AbsDiff::default().epsilon(f64::EPSILON).eq(&1.0, &1.0);
188/// ```
189pub struct AbsDiff<A, B = A>
190where
191 A: AbsDiffEq<B> + ?Sized,
192 B: ?Sized,
193{
194 /// The tolerance to use when testing values that are close together.
195 pub epsilon: A::Epsilon,
196}
197
198impl<A, B> Default for AbsDiff<A, B>
199where
200 A: AbsDiffEq<B> + ?Sized,
201 B: ?Sized,
202{
203 #[inline]
204 fn default() -> AbsDiff<A, B> {
205 AbsDiff {
206 epsilon: A::default_epsilon(),
207 }
208 }
209}
210
211impl<A, B> AbsDiff<A, B>
212where
213 A: AbsDiffEq<B> + ?Sized,
214 B: ?Sized,
215{
216 /// Replace the epsilon value with the one specified.
217 #[inline]
218 pub fn epsilon(self, epsilon: A::Epsilon) -> AbsDiff<A, B> {
219 AbsDiff { epsilon }
220 }
221
222 /// Peform the equality comparison
223 #[inline]
224 #[must_use]
225 pub fn eq(self, lhs: &A, rhs: &B) -> bool {
226 A::abs_diff_eq(lhs, rhs, self.epsilon)
227 }
228
229 /// Peform the inequality comparison
230 #[inline]
231 #[must_use]
232 pub fn ne(self, lhs: &A, rhs: &B) -> bool {
233 A::abs_diff_ne(lhs, rhs, self.epsilon)
234 }
235}
236
237/// The requisite parameters for testing for approximate equality using a
238/// relative based comparison.
239///
240/// This is not normally used directly, rather via the
241/// `assert_relative_{eq|ne}!` and `relative_{eq|ne}!` macros.
242///
243/// # Example
244///
245/// ```rust
246/// use std::f64;
247/// use approx::Relative;
248///
249/// Relative::default().eq(&1.0, &1.0);
250/// Relative::default().epsilon(f64::EPSILON).eq(&1.0, &1.0);
251/// Relative::default().max_relative(1.0).eq(&1.0, &1.0);
252/// Relative::default().epsilon(f64::EPSILON).max_relative(1.0).eq(&1.0, &1.0);
253/// Relative::default().max_relative(1.0).epsilon(f64::EPSILON).eq(&1.0, &1.0);
254/// ```
255pub struct Relative<A, B = A>
256where
257 A: RelativeEq<B> + ?Sized,
258 B: ?Sized,
259{
260 /// The tolerance to use when testing values that are close together.
261 pub epsilon: A::Epsilon,
262 /// The relative tolerance for testing values that are far-apart.
263 pub max_relative: A::Epsilon,
264}
265
266impl<A, B> Default for Relative<A, B>
267where
268 A: RelativeEq<B> + ?Sized,
269 B: ?Sized,
270{
271 #[inline]
272 fn default() -> Relative<A, B> {
273 Relative {
274 epsilon: A::default_epsilon(),
275 max_relative: A::default_max_relative(),
276 }
277 }
278}
279
280impl<A, B> Relative<A, B>
281where
282 A: RelativeEq<B> + ?Sized,
283 B: ?Sized,
284{
285 /// Replace the epsilon value with the one specified.
286 #[inline]
287 pub fn epsilon(self, epsilon: A::Epsilon) -> Relative<A, B> {
288 Relative { epsilon, ..self }
289 }
290
291 /// Replace the maximum relative value with the one specified.
292 #[inline]
293 pub fn max_relative(self, max_relative: A::Epsilon) -> Relative<A, B> {
294 Relative {
295 max_relative,
296 ..self
297 }
298 }
299
300 /// Peform the equality comparison
301 #[inline]
302 #[must_use]
303 pub fn eq(self, lhs: &A, rhs: &B) -> bool {
304 A::relative_eq(lhs, rhs, self.epsilon, self.max_relative)
305 }
306
307 /// Peform the inequality comparison
308 #[inline]
309 #[must_use]
310 pub fn ne(self, lhs: &A, rhs: &B) -> bool {
311 A::relative_ne(lhs, rhs, self.epsilon, self.max_relative)
312 }
313}
314
315/// The requisite parameters for testing for approximate equality using an ULPs
316/// based comparison.
317///
318/// This is not normally used directly, rather via the `assert_ulps_{eq|ne}!`
319/// and `ulps_{eq|ne}!` macros.
320///
321/// # Example
322///
323/// ```rust
324/// use std::f64;
325/// use approx::Ulps;
326///
327/// Ulps::default().eq(&1.0, &1.0);
328/// Ulps::default().epsilon(f64::EPSILON).eq(&1.0, &1.0);
329/// Ulps::default().max_ulps(4).eq(&1.0, &1.0);
330/// Ulps::default().epsilon(f64::EPSILON).max_ulps(4).eq(&1.0, &1.0);
331/// Ulps::default().max_ulps(4).epsilon(f64::EPSILON).eq(&1.0, &1.0);
332/// ```
333pub struct Ulps<A, B = A>
334where
335 A: UlpsEq<B> + ?Sized,
336 B: ?Sized,
337{
338 /// The tolerance to use when testing values that are close together.
339 pub epsilon: A::Epsilon,
340 /// The ULPs to tolerate when testing values that are far-apart.
341 pub max_ulps: u32,
342}
343
344impl<A, B> Default for Ulps<A, B>
345where
346 A: UlpsEq<B> + ?Sized,
347 B: ?Sized,
348{
349 #[inline]
350 fn default() -> Ulps<A, B> {
351 Ulps {
352 epsilon: A::default_epsilon(),
353 max_ulps: A::default_max_ulps(),
354 }
355 }
356}
357
358impl<A, B> Ulps<A, B>
359where
360 A: UlpsEq<B> + ?Sized,
361 B: ?Sized,
362{
363 /// Replace the epsilon value with the one specified.
364 #[inline]
365 pub fn epsilon(self, epsilon: A::Epsilon) -> Ulps<A, B> {
366 Ulps { epsilon, ..self }
367 }
368
369 /// Replace the max ulps value with the one specified.
370 #[inline]
371 pub fn max_ulps(self, max_ulps: u32) -> Ulps<A, B> {
372 Ulps { max_ulps, ..self }
373 }
374
375 /// Peform the equality comparison
376 #[inline]
377 #[must_use]
378 pub fn eq(self, lhs: &A, rhs: &B) -> bool {
379 A::ulps_eq(lhs, rhs, self.epsilon, self.max_ulps)
380 }
381
382 /// Peform the inequality comparison
383 #[inline]
384 #[must_use]
385 pub fn ne(self, lhs: &A, rhs: &B) -> bool {
386 A::ulps_ne(lhs, rhs, self.epsilon, self.max_ulps)
387 }
388}