blob: 6e453732056392a5e1523e8b44005d06626a29ed [file] [log] [blame]
// Copyright 2015 Brendan Zabarauskas
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! A crate that provides facilities for testing the approximate equality of floating-point
//! based types, using either relative difference, or units in the last place (ULPs)
//! comparisons.
//!
//! You can also use the `approx_{eq, ne}!` `assert_approx_{eq, ne}!` macros to test for equality
//! using a more positional style.
//!
//! ```rust
//! #[macro_use]
//! extern crate approx;
//!
//! use std::f64;
//!
//! # fn main() {
//! abs_diff_eq!(1.0, 1.0);
//! abs_diff_eq!(1.0, 1.0, epsilon = f64::EPSILON);
//!
//! relative_eq!(1.0, 1.0);
//! relative_eq!(1.0, 1.0, epsilon = f64::EPSILON);
//! relative_eq!(1.0, 1.0, max_relative = 1.0);
//! relative_eq!(1.0, 1.0, epsilon = f64::EPSILON, max_relative = 1.0);
//! relative_eq!(1.0, 1.0, max_relative = 1.0, epsilon = f64::EPSILON);
//!
//! ulps_eq!(1.0, 1.0);
//! ulps_eq!(1.0, 1.0, epsilon = f64::EPSILON);
//! ulps_eq!(1.0, 1.0, max_ulps = 4);
//! ulps_eq!(1.0, 1.0, epsilon = f64::EPSILON, max_ulps = 4);
//! ulps_eq!(1.0, 1.0, max_ulps = 4, epsilon = f64::EPSILON);
//! # }
//! ```
//!
//! # Implementing approximate equality for custom types
//!
//! The `ApproxEq` trait allows approximate equalities to be implemented on types, based on the
//! fundamental floating point implementations.
//!
//! For example, we might want to be able to do approximate assertions on a complex number type:
//!
//! ```rust
//! #[macro_use]
//! extern crate approx;
//! # use approx::{AbsDiffEq, RelativeEq, UlpsEq};
//!
//! #[derive(Debug, PartialEq)]
//! struct Complex<T> {
//! x: T,
//! i: T,
//! }
//! # impl<T: AbsDiffEq> AbsDiffEq for Complex<T> where T::Epsilon: Copy {
//! # type Epsilon = T::Epsilon;
//! # fn default_epsilon() -> T::Epsilon { T::default_epsilon() }
//! # fn abs_diff_eq(&self, other: &Self, epsilon: T::Epsilon) -> bool {
//! # T::abs_diff_eq(&self.x, &other.x, epsilon) &&
//! # T::abs_diff_eq(&self.i, &other.i, epsilon)
//! # }
//! # }
//! # impl<T: RelativeEq> RelativeEq for Complex<T> where T::Epsilon: Copy {
//! # fn default_max_relative() -> T::Epsilon { T::default_max_relative() }
//! # fn relative_eq(&self, other: &Self, epsilon: T::Epsilon, max_relative: T::Epsilon)
//! # -> bool {
//! # T::relative_eq(&self.x, &other.x, epsilon, max_relative) &&
//! # T::relative_eq(&self.i, &other.i, epsilon, max_relative)
//! # }
//! # }
//! # impl<T: UlpsEq> UlpsEq for Complex<T> where T::Epsilon: Copy {
//! # fn default_max_ulps() -> u32 { T::default_max_ulps() }
//! # fn ulps_eq(&self, other: &Self, epsilon: T::Epsilon, max_ulps: u32) -> bool {
//! # T::ulps_eq(&self.x, &other.x, epsilon, max_ulps) &&
//! # T::ulps_eq(&self.i, &other.i, epsilon, max_ulps)
//! # }
//! # }
//!
//! # fn main() {
//! let x = Complex { x: 1.2, i: 2.3 };
//!
//! assert_relative_eq!(x, x);
//! assert_ulps_eq!(x, x, max_ulps = 4);
//! # }
//! ```
//!
//! To do this we can implement `AbsDiffEq`, `RelativeEq` and `UlpsEq` generically in terms of a
//! type parameter that also implements `ApproxEq`, `RelativeEq` and `UlpsEq` respectively. This
//! means that we can make comparisons for either `Complex<f32>` or `Complex<f64>`:
//!
//! ```rust
//! # use approx::{AbsDiffEq, RelativeEq, UlpsEq};
//! # #[derive(Debug, PartialEq)]
//! # struct Complex<T> { x: T, i: T, }
//! #
//! impl<T: AbsDiffEq> AbsDiffEq for Complex<T> where
//! T::Epsilon: Copy,
//! {
//! type Epsilon = T::Epsilon;
//!
//! fn default_epsilon() -> T::Epsilon {
//! T::default_epsilon()
//! }
//!
//! fn abs_diff_eq(&self, other: &Self, epsilon: T::Epsilon) -> bool {
//! T::abs_diff_eq(&self.x, &other.x, epsilon) &&
//! T::abs_diff_eq(&self.i, &other.i, epsilon)
//! }
//! }
//!
//! impl<T: RelativeEq> RelativeEq for Complex<T> where
//! T::Epsilon: Copy,
//! {
//! fn default_max_relative() -> T::Epsilon {
//! T::default_max_relative()
//! }
//!
//! fn relative_eq(&self, other: &Self, epsilon: T::Epsilon, max_relative: T::Epsilon) -> bool {
//! T::relative_eq(&self.x, &other.x, epsilon, max_relative) &&
//! T::relative_eq(&self.i, &other.i, epsilon, max_relative)
//! }
//! }
//!
//! impl<T: UlpsEq> UlpsEq for Complex<T> where
//! T::Epsilon: Copy,
//! {
//! fn default_max_ulps() -> u32 {
//! T::default_max_ulps()
//! }
//!
//! fn ulps_eq(&self, other: &Self, epsilon: T::Epsilon, max_ulps: u32) -> bool {
//! T::ulps_eq(&self.x, &other.x, epsilon, max_ulps) &&
//! T::ulps_eq(&self.i, &other.i, epsilon, max_ulps)
//! }
//! }
//! ```
//!
//! # References
//!
//! Floating point is hard! Thanks goes to these links for helping to make things a _little_
//! easier to understand:
//!
//! - [Comparing Floating Point Numbers, 2012 Edition]
//! (https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/)
//! - [The Floating Point Guide - Comparison](http://floating-point-gui.de/errors/comparison/)
//! - [What Every Computer Scientist Should Know About Floating-Point Arithmetic]
//! (https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html)
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "num-complex")]
extern crate num_complex;
extern crate num_traits;
#[cfg(not(feature = "std"))]
use core as std;
mod abs_diff_eq;
mod relative_eq;
mod ulps_eq;
mod macros;
pub use abs_diff_eq::AbsDiffEq;
pub use relative_eq::RelativeEq;
pub use ulps_eq::UlpsEq;
/// The requisite parameters for testing for approximate equality using a
/// absolute difference based comparison.
///
/// This is not normally used directly, rather via the
/// `assert_abs_diff_{eq|ne}!` and `abs_diff_{eq|ne}!` macros.
///
/// # Example
///
/// ```rust
/// use std::f64;
/// use approx::AbsDiff;
///
/// AbsDiff::default().eq(&1.0, &1.0);
/// AbsDiff::default().epsilon(f64::EPSILON).eq(&1.0, &1.0);
/// ```
pub struct AbsDiff<T: AbsDiffEq + ?Sized> {
/// The tolerance to use when testing values that are close together.
pub epsilon: T::Epsilon,
}
impl<T: AbsDiffEq + ?Sized> Default for AbsDiff<T> {
#[inline]
fn default() -> AbsDiff<T> {
AbsDiff {
epsilon: T::default_epsilon(),
}
}
}
impl<T> AbsDiff<T>
where
T: AbsDiffEq + ?Sized,
{
/// Replace the epsilon value with the one specified.
#[inline]
pub fn epsilon(self, epsilon: T::Epsilon) -> AbsDiff<T> {
AbsDiff { epsilon, ..self }
}
/// Peform the equality comparison
#[inline]
pub fn eq(self, lhs: &T, rhs: &T) -> bool {
T::abs_diff_eq(lhs, rhs, self.epsilon)
}
/// Peform the inequality comparison
#[inline]
pub fn ne(self, lhs: &T, rhs: &T) -> bool {
T::abs_diff_ne(lhs, rhs, self.epsilon)
}
}
/// The requisite parameters for testing for approximate equality using a
/// relative based comparison.
///
/// This is not normally used directly, rather via the
/// `assert_relative_{eq|ne}!` and `relative_{eq|ne}!` macros.
///
/// # Example
///
/// ```rust
/// use std::f64;
/// use approx::Relative;
///
/// Relative::default().eq(&1.0, &1.0);
/// Relative::default().epsilon(f64::EPSILON).eq(&1.0, &1.0);
/// Relative::default().max_relative(1.0).eq(&1.0, &1.0);
/// Relative::default().epsilon(f64::EPSILON).max_relative(1.0).eq(&1.0, &1.0);
/// Relative::default().max_relative(1.0).epsilon(f64::EPSILON).eq(&1.0, &1.0);
/// ```
pub struct Relative<T: RelativeEq + ?Sized> {
/// The tolerance to use when testing values that are close together.
pub epsilon: T::Epsilon,
/// The relative tolerance for testing values that are far-apart.
pub max_relative: T::Epsilon,
}
impl<T: RelativeEq + ?Sized> Default for Relative<T> {
#[inline]
fn default() -> Relative<T> {
Relative {
epsilon: T::default_epsilon(),
max_relative: T::default_max_relative(),
}
}
}
impl<T: RelativeEq + ?Sized> Relative<T> {
/// Replace the epsilon value with the one specified.
#[inline]
pub fn epsilon(self, epsilon: T::Epsilon) -> Relative<T> {
Relative { epsilon, ..self }
}
/// Replace the maximum relative value with the one specified.
#[inline]
pub fn max_relative(self, max_relative: T::Epsilon) -> Relative<T> {
Relative {
max_relative,
..self
}
}
/// Peform the equality comparison
#[inline]
pub fn eq(self, lhs: &T, rhs: &T) -> bool {
T::relative_eq(lhs, rhs, self.epsilon, self.max_relative)
}
/// Peform the inequality comparison
#[inline]
pub fn ne(self, lhs: &T, rhs: &T) -> bool {
T::relative_ne(lhs, rhs, self.epsilon, self.max_relative)
}
}
/// The requisite parameters for testing for approximate equality using an ULPs
/// based comparison.
///
/// This is not normally used directly, rather via the `assert_ulps_{eq|ne}!`
/// and `ulps_{eq|ne}!` macros.
///
/// # Example
///
/// ```rust
/// use std::f64;
/// use approx::Ulps;
///
/// Ulps::default().eq(&1.0, &1.0);
/// Ulps::default().epsilon(f64::EPSILON).eq(&1.0, &1.0);
/// Ulps::default().max_ulps(4).eq(&1.0, &1.0);
/// Ulps::default().epsilon(f64::EPSILON).max_ulps(4).eq(&1.0, &1.0);
/// Ulps::default().max_ulps(4).epsilon(f64::EPSILON).eq(&1.0, &1.0);
/// ```
pub struct Ulps<T: UlpsEq + ?Sized> {
/// The tolerance to use when testing values that are close together.
pub epsilon: T::Epsilon,
/// The ULPs to tolerate when testing values that are far-apart.
pub max_ulps: u32,
}
impl<T: UlpsEq + ?Sized> Default for Ulps<T>
where
T: UlpsEq,
{
#[inline]
fn default() -> Ulps<T> {
Ulps {
epsilon: T::default_epsilon(),
max_ulps: T::default_max_ulps(),
}
}
}
impl<T: UlpsEq + ?Sized> Ulps<T> {
/// Replace the epsilon value with the one specified.
#[inline]
pub fn epsilon(self, epsilon: T::Epsilon) -> Ulps<T> {
Ulps { epsilon, ..self }
}
/// Replace the max ulps value with the one specified.
#[inline]
pub fn max_ulps(self, max_ulps: u32) -> Ulps<T> {
Ulps { max_ulps, ..self }
}
/// Peform the equality comparison
#[inline]
pub fn eq(self, lhs: &T, rhs: &T) -> bool {
T::ulps_eq(lhs, rhs, self.epsilon, self.max_ulps)
}
/// Peform the inequality comparison
#[inline]
pub fn ne(self, lhs: &T, rhs: &T) -> bool {
T::ulps_ne(lhs, rhs, self.epsilon, self.max_ulps)
}
}