Expand description
A crate that provides facilities for testing the approximate equality of floating-point based types, using either absolute, relative differences or units in the last place (ULPs) comparisons.
§Macros
The crate provides macros in the form of *_{eq, ne}! and assert_*_{eq, ne}! to test for
equality using a positional style.
The former return a bool while the latter will panic.
These macros are also available as a debug_*_{eq, ne} and debug_assert_*_{eq,ne} version
which are only executed when cfg(debug_assertions) is active.
x≈y -> bool | x!≈y -> bool | assert!(x≈y) | assert!(x!≈y) | |
|---|---|---|---|---|
AbsDiffEq | abs_diff_eq | abs_diff_ne | assert_abs_diff_eq | assert_abs_diff_ne |
RelativeEq | relative_eq | relative_ne | assert_relative_eq | assert_relative_ne |
Ulps | ulps_eq | ulps_ne | assert_ulps_eq | assert_ulps_ne |
static ε: f64 = f64::EPSILON;
assert_abs_diff_eq!(1.0, 1.0); // ✅
assert_abs_diff_eq!(1.0, 1.0 + ε); // ✅ default: epsilon = f64::EPSILON
assert_abs_diff_ne!(1.0, 1.0 + ε+ε); // ❌ diff (2ε) exceeds default (ε); assert "ne" instead of "eq"
assert_abs_diff_eq!(1.0, 1.0 + ε+ε, epsilon = ε+ε); // ✅ diff (2ε) ≤ "epsilon" param (2ε)
assert_relative_eq!(1.0, 1.0); // ✅ compare abs(a - b) / max(a, b) to default (f64::EPSILON)
assert_relative_ne!(1.0, 1.1); // ❌ 0.1/1.1 ≥ ε (assert "ne" instead of "eq")
assert_relative_eq!(1.0, 1.1, max_relative = 0.1); // ✅ 0.1/1.1 < 0.1
assert_relative_eq!(1.1, 1.0, max_relative = 0.1); // ✅ order doesn't matter, cmp is commutative
assert_relative_ne!(1.0, 1.2, max_relative = 0.1); // ❌ 0.2/1.2 > 0.1
assert_relative_ne!(0.0, 1e-6, max_relative = 1e-5); // ❌ maximum possible relative diff is 1.0 (when one side is 0)
assert_relative_eq!(0.0, 1e-6, epsilon = 1e-5, max_relative = 1e-5); // ✅ passing `epsilon` allows short-circuiting based on small abs diff
assert_ulps_eq!(1., 1. + 1e-17); // ✅ default: max_ulps = 4
assert_ulps_eq!(1., 1. + 1e-16); // ✅ ""
assert_ulps_ne!(1., 1. + 1e-15); // ❌ assert "ne" instead of "eq"
assert_ulps_eq!(1., 1. + 1e-15, max_ulps = 5); // ✅ relaxed max_ulps§Deriving Traits
The [approx_derive] crate was explicitly designed to provide derive macros for the approximate
comparison traits.
It is generally advised to use this crate before manually implementing the functionality.
Deriving traits will perform the actions of the respective comparison trait on the individual
struct fields one after the other.
This requires the derive feature.
use approx::{AbsDiffEq, assert_abs_diff_eq};
#[derive(AbsDiffEq, Debug, PartialEq)]
struct Barrel<T> {
radius: T,
height: T,
#[approx(equal)]
color: [u8; 3],
}
let v1 = Barrel { radius: 43.0, height: 72.0, color: [255, 0, 0], };
let v2 = Barrel { radius: 42.9, height: 72.1, color: [255, 0, 0], };
assert_abs_diff_eq!(v1, v2, epsilon = 0.11f64);§Implementing approximate equality for custom types
The *Eq traits allow 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:
#[derive(Debug, PartialEq)]
struct Complex<T> {
x: T,
i: T,
}
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 AbsDiffEq, RelativeEq and UlpsEq respectively.
This means that we can make comparisons for either Complex<f32> or Complex<f64>:
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)
}
}§Supported Types
AbsDiffEq | RelativeEq | UlpsEq | Comment | |
|---|---|---|---|---|
f32, f64 | ✅ | ✅ | ✅ | |
i8, i16, i32, i64,i128,isize | ✅ | ✅ | ❌ | |
u8, u16, u32, u64,u128,usize | ✅ | ✅ | ❌ | |
Option<T> | ✅ | ✅ | ✅ | |
Result<T, E> | ✅ | ✅ | ✅ | |
Vec<T> | ✅ | ✅ | ✅ | feature vec_impl |
indexmap::IndexMap<K, V, S> | ✅ | ✅ | ✅ | feature indexmap_impl |
num_complex::Complex<T> | ✅ | ✅ | ✅ | feature num_complex |
ordered_float::OrderedFloat<T> | ✅ | ✅ | ✅ | feature ordered_float |
[T; N] | ✅ | ✅ | ✅ | feature array_impl |
(T0, T1, .., T11) | ✅ | ✅ | ✅ | feature tuple_impl, up to size 12 |
§References
Floating point is hard! Thanks goes to these links for helping to make things a little easier to understand:
Macros§
- abs_
diff_ eq - Approximate equality of using the absolute difference.
- abs_
diff_ ne - Approximate inequality of using the absolute difference.
- assert_
abs_ diff_ eq - An assertion that delegates to
abs_diff_eq!, and panics with a helpful error on failure. - assert_
abs_ diff_ ne - An assertion that delegates to
abs_diff_ne!, and panics with a helpful error on failure. - assert_
relative_ eq - An assertion that delegates to
relative_eq!, and panics with a helpful error on failure. - assert_
relative_ ne - An assertion that delegates to
relative_ne!, and panics with a helpful error on failure. - assert_
ulps_ eq - An assertion that delegates to
ulps_eq!, and panics with a helpful error on failure. - assert_
ulps_ ne - An assertion that delegates to
ulps_ne!, and panics with a helpful error on failure. - debug_
abs_ diff_ eq - Debug version of
abs_diff_eq. - debug_
abs_ diff_ ne - Debug version of
abs_diff_ne. - debug_
assert_ abs_ diff_ eq - Debug version of
assert_abs_diff_eq - debug_
assert_ abs_ diff_ ne - Debug version of
assert_abs_diff_ne - debug_
assert_ relative_ eq - Debug version of
assert_relative_eq - debug_
assert_ relative_ ne - Debug version of
assert_relative_ne - debug_
assert_ ulps_ eq - Debug version of
assert_ulps_eq - debug_
assert_ ulps_ ne - Debug version of
assert_ulps_ne - debug_
relative_ eq - Debug version of
relative_eq. - debug_
relative_ ne - Debug version of
relative_ne. - debug_
ulps_ eq - Debug version of
ulps_eq. - debug_
ulps_ ne - Debug version of
ulps_ne. - relative_
eq - Approximate equality using both the absolute difference and relative based comparisons.
- relative_
ne - Approximate inequality using both the absolute difference and relative based comparisons.
- ulps_eq
- Approximate equality using both the absolute difference and ULPs (Units in Last Place).
- ulps_ne
- Approximate inequality using both the absolute difference and ULPs (Units in Last Place).
Structs§
- AbsDiff
- The requisite parameters for testing for approximate equality using a absolute difference based comparison.
- Relative
- The requisite parameters for testing for approximate equality using a relative based comparison.
- Ulps
- The requisite parameters for testing for approximate equality using an ULPs based comparison.
Traits§
- AbsDiff
Eq - Equality that is defined using the absolute difference of two numbers.
- Relative
Eq - Equality comparisons between two numbers using both the absolute difference and relative based comparisons.
- UlpsEq
- Equality comparisons between two numbers using both the absolute difference and ULPs (Units in Last Place) based comparisons.