jtempest / float_eq-rs

Compare IEEE floating point values for equality.
Other
39 stars 6 forks source link
floating-point rust testing

float_eq

Build codecov crate documentation

Compare IEEE floating point primitives, structs and collections for equality.

This crate provides an API with a focus on making the choices of comparison algorithm(s) and tolerances intuitive to implementers and maintainers, and of providing clear output for debugging and development iteration.

This readme is a quick tour of the crate. For introductory material, guides and discussion see the float_eq guide.

Usage

Add this to your cargo.toml:

[dependencies]
float_eq = "1"

And, if you're using the 2015 edition, this to your crate root:

extern crate float_eq;

Then, you can import items with use:

use float_eq::{assert_float_eq, float_eq};

Comparisons

This crate provides boolean comparison operations:

if (float_eq!(y_pos, 0.0, abs <= 0.000_1)) {
    //...
}

And asserts:

const RECIP_REL_TOL: f32 = 0.000_366_210_94;
assert_float_eq!(x.recip(), 10.0, r2nd <= RECIP_REL_TOL);

Using absolute tolerance, relative tolerance or ULPs based [comparison algorithms].

Composite types

Composite types may implement the provided extension traits to be compared on a field-by-field basis:

let a = Complex32 { re: 2.0, im: 4.000_002 };
let b = Complex32 { re: 2.000_000_5, im: 4.0 };

assert_float_eq!(a, b, ulps <= ComplexUlps32 { re: 2, im: 4 });

...and if they are homogeneous, with a uniformly applied tolerance across all fields:

assert_float_eq!(a, b, ulps_all <= 4);

Arrays of any size are supported:

let a = [1.0, -2.0, 3.0];
let b = [-1.0, 2.0, 3.5];
assert_float_eq!(a, b, abs <= [2.0, 4.0, 0.5]);
assert_float_eq!(a, b, abs_all <= 4.0);

As are tuples up to size 12 (inclusive):

let a = (1.0f32, 2.0f64);
let b = (1.5f32, -2.0f64);
assert_float_eq!(a, b, r2nd <= (0.5, 2.0));

Many standard and core types like Vec are supported:

let a = vec![1.0, -2.0, 3.0];
let b = vec![-1.0, 2.0, 3.5];
assert_float_eq!(a, b, rmax <= vec![2.0, 2.0, 0.25]);
assert_float_eq!(a, b, rmax_all <= 2.0);

There are blanket trait impls for comparing mutable and immutable reference types, the contents of Cell, RefCell, Rc, Arc and Box instances, as well as for slices, Option, Vec, VecDeque, LinkedList, BTreeMap and HashMap.

Derivable

The extension traits may be derived for non-generic structs and tuple structs:

#[derive_float_eq(
    ulps_tol = "PointUlps",
    ulps_tol_derive = "Clone, Copy, Debug, PartialEq",
    debug_ulps_diff = "PointUlpsDebugUlpsDiff",
    debug_ulps_diff_derive = "Clone, Copy, Debug, PartialEq",
    all_tol = "f64"
)]
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Point {
    pub x: f64,
    pub y: f64,
}

let a = Point { x: 1.0, y: -2.0 };
let c = Point { 
    x: 1.000_000_000_000_000_9, 
    y: -2.000_000_000_000_001_3
};
assert_float_eq!(a, c, ulps <= PointUlps { x: 4, y: 3 });
assert_float_eq!(a, c, ulps_all <= 4);

Error messages

Asserts provide additional useful context information. For example:

assert_float_eq!(4.0f32, 4.000_008, rmax <= 0.000_001);

Panics with this error message:

thread 'main' panicked at 'assertion failed: `float_eq!(left, right, rmax <= t)`
        left: `4.0`,
       right: `4.000008`,
    abs_diff: `0.000008106232`,
   ulps_diff: `Some(17)`,
    [rmax] t: `0.000004000008`', assert_failure.rs:15:5

Where [rmax] t shows the tolerance value that the absolute difference was compared against after being appropriately scaled.

Optional features

This crate can be used without the standard library (#![no_std]) by disabling the default std feature. Use this in Cargo.toml:

[dependencies.float_eq]
version = "1"
default-features = false

Other optional features:

Related efforts

The approx, float-cmp, assert_float_eq and is_close crates provide similar floating point comparison capabilities to float_eq. The almost crate divides its API into comparison of floats against zero and non-zero values. The efloat crate provides an f32 equivalent type that tracks the maximum possible error bounds that may have occured due to rounding.

The ieee754 crate is not a comparison library, but provides useful functionality for decomposing floats into their component parts, iterating over representable values and working with ULPs directly, amoung other things.

Contributing

Constructive feedback, suggestions and contributions welcomed, please open an issue.

Changelog

Release information is available in CHANGELOG.md.


License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.


Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in float_eq by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.