4 releases (breaking)

0.4.0 Apr 8, 2024
0.3.0 Feb 19, 2024
0.2.0 May 15, 2023
0.1.0 Mar 14, 2023

#115 in Debugging

Download history 23/week @ 2024-02-11 142/week @ 2024-02-18 38/week @ 2024-02-25 1/week @ 2024-03-03 14/week @ 2024-03-10 1/week @ 2024-03-17 52/week @ 2024-03-31 99/week @ 2024-04-07

153 downloads per month

MIT license

120KB
2K SLoC

nearly

license  crate  doc  ci

Compare IEEE floating point types by nearly comparisons.

When comparing floating point types, because of their limited precision, they might not be exactly identical. Consider the following example, where a and b appear to be identical, but they are not:

let a: f32 = 1.0 + 1.04 + 1.1;
let b: f32 = 3.14;

assert!(a == b); // <-- PANICS

This crate provides macros to perform a comparison with some tolerance.

use nearly::nearly;
assert!( nearly!(a == b) ); // <-- OK

Usage

The easiest way to use nearly comparisons is by invoking the nearly! macro. The macro returns a boolean whether the comparison is true or false by using the provided tolerance.

The comparison can be:

  • a == b for testing whether a is nearly equal to b
  • a != b for testing whether a is not nearly equal to b
  • a < b for testing whether a is strict less than b but not nearly equal to b
  • a <= b for testing whether a is strict less than b or nearly equal to b
  • a > b for testing whether a is strict greater than b but not nearly equal to b
  • a >= b for testing whether a is strict greater than b or nearly equal to b

The tolerance used can be:

  • eps for an absolute epsilon tolerance
  • ulps for an ulps based tolerance
  • tol for an absolute epsilon and ulps based tolerance
  • default for an absolute epsilon and ulps based tolerance using default values

Here are some example calls:

use nearly::{nearly, Tolerance};

let a: f32 = 1.0 + 1.04 + 1.1;
let b: f32 = 3.14;

// use absolute epsilon tolerance
nearly!(a == b, eps = 0.001);

// use ulps based tolerance
nearly!(a == b, ulps = 5);

// use absolute epsilon and ulps based tolerance
nearly!(a == b, eps = 0.001, ulps = 5);
nearly!(a == b, tol = Tolerance::new(0.001, 5));

// use default absolute epsilon and default ulps based tolerance
nearly!(a == b);

There is also an assert_nearly! and debug_assert_nearly! macro you can use that panic if the nearly comparison evaluates to false. The signature is the same as for the nearly! macro.

use nearly::{assert_nearly, debug_assert_nearly, Tolerance};

assert_nearly!(a == b, eps = 0.001);
assert_nearly!(a == b, ulps = 5);
assert_nearly!(a == b, eps = 0.001, ulps = 5);
assert_nearly!(a == b, tol = Tolerance::new(0.001, 5));
assert_nearly!(a == b);

debug_assert_nearly!(a == b, eps = 0.001);
debug_assert_nearly!(a == b, ulps = 5);
debug_assert_nearly!(a == b, eps = 0.001, ulps = 5);
debug_assert_nearly!(a == b, tol = Tolerance::new(0.001, 5));
debug_assert_nearly!(a == b);

The nearly functionality is also implemented for a variety of other types holding floats like containers, maps, pointers or tuples. Here is an example of comparing two arrays of floats.

use nearly::nearly;

let a: [f32; 4] = [1.1, 2.2, 2.2, 4.4];
let b: [f32; 4] = [1.1, 2.2, 3.3, 4.4];

nearly!(a <= b, eps = 0.001, ulps = 5);

Derive the nearly traits

The easiest way to add nearly comparison to your own types is by deriving the nearly traits. Just derive NearlyEq and NearlyOrd to get full support on your type.

use nearly::{assert_nearly, NearlyEq, NearlyOrd};

#[derive(Debug, NearlyEq, NearlyOrd)]
struct Point {
    x: f32,
    y: f32,
}

let a = Point { x: 1.23, y: 4.56 };
let b = Point { x: 1.23, y: 4.567 };

assert_nearly!(a == b, eps = 0.01);
assert_nearly!(a <= b, eps = 0.01);

To use the assert_nearly! and debug_assert_nearly! macros, your type must also implement the Debug trait.

You can derive the following traits:

  • NearlyEqEps: enables nearly support with absolute epsilon tolerance
  • NearlyEqUlps: enables nearly support with ulps based tolerance
  • NearlyEqTol: enables nearly support with absolute epsilon and ulps based tolerances
  • NearlyEq: enables nearly support with absolute epsilon and ulps based tolerances with default values
  • NearlyOrdEps: enables nearly ordering support with absolute epsilon tolerance
  • NearlyOrdUlps: enables nearly ordering support with ulps based tolerance
  • NearlyOrdTol: enables nearly ordering support with absolute epsilon and ulps based tolerances
  • NearlyOrd: enables nearly ordering support with absolute epsilon and ulps based tolerances with default values

Dependencies

~0.4–0.9MB
~20K SLoC