Skip to main content

Crate approx

Crate approx 

Source
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.

  1. Macros
  2. Derive Macros
  3. Custom Implementations
  4. Supported Types
  5. References

§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.

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

AbsDiffEqRelativeEqUlpsEqComment
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§

AbsDiffEq
Equality that is defined using the absolute difference of two numbers.
RelativeEq
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.