Crate approxim

Source
Expand description

A crate that provides facilities for testing the approximimate equality of floating-point based types, using either relative difference, or units in the last place (ULPs) comparisons.

You can also use the *_{eq, ne}! and assert_*_{eq, ne}! macros to test for equality using a more positional style:

#[macro_use]
extern crate approxim;

use std::f64;

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

See also the abs_diff_eq!, relative_eq! and ulps_eq! macros, which return bool instead of asserting.

§Implementing approximate equality for custom types

The *Eq traits allow approximimate equalities to be implemented on types, based on the fundamental floating point implementations.

For example, we might want to be able to do approximimate assertions on a complex number type:

#[macro_use]
extern crate approxim;

#[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)
    }
}

§References

Floating point is hard! Thanks goes to these links for helping to make things a little easier to understand:

Modules§

approx_derivederive
This crate provides derive macros for the AbsDiffEq and RelativeEq traits of the approxim crate.

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.
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 approximimate equality using a absolute difference based comparison.
Relative
The requisite parameters for testing for approximimate equality using a relative based comparison.
Ulps
The requisite parameters for testing for approximimate 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.

Derive Macros§

AbsDiffEqderive
See approx_derive
RelativeEqderive
See approx_derive