3 releases (breaking)

0.2.0 Aug 11, 2019
0.1.0 Jun 14, 2019
0.0.1 Jun 14, 2019

#577 in Rust patterns

Download history 116/week @ 2021-10-06 110/week @ 2021-10-13 149/week @ 2021-10-20 486/week @ 2021-10-27 1302/week @ 2021-11-03 884/week @ 2021-11-10 1177/week @ 2021-11-17 1675/week @ 2021-11-24 2262/week @ 2021-12-01 4250/week @ 2021-12-08 2688/week @ 2021-12-15 1326/week @ 2021-12-22 1964/week @ 2021-12-29 2786/week @ 2022-01-05 4141/week @ 2022-01-12 3602/week @ 2022-01-19

12,725 downloads per month
Used in 2 crates

CC0 license

32KB
124 lines

almost: A crate for comparing floating point numbers.

Docs

This crate strives to make choices that are good for most cases, and not providing you a complex API that encourages misuse.

  1. Uses relative comparison by default.
  2. Provides a separate function for comparison with zero (the only time when absolute comparison is a good default choice).
  3. Uses a better default for tolerance than std::{f32,f64}::EPSILON.
  4. Handles infinities / subnormals properly.
  5. no_std compatible always

License

Public domain, as explained here


lib.rs:

A crate to test if floats are almost equal.

# let (x, y, z) = (0.5, 1.0, 2.0);
// Compare two variables.
if almost::equal(x, y) {
   println!("They're almost equal!");
}
// Or, if you need need to compare with a constant zero:
if almost::zero(z) {
   println!("It's almost zero!");
}

Why another crate?

There are a lot of crates for doing this already.

The author crate has fairly strong opinions on how this should be done, and thinks most of the similar crates in the wild make dubious choices, make it easy for the user to misuse, or follow poor numerical robustness practices.

Specific differences / benefits compared to other crates:

  1. Better choice of default tolerances for unknown inputs. Often the value of the EPSILON is used as a value for tolerance, or its use is encouraged by the API).

    This is the wrong choice more often than it is right. The machine epsilon is a quite strict bound for comparison, and after just a few arithmetic operations you will no longer be within it.

    This library chooses a default tolerance value that is much more forgiving while still tight enough for it to be unlikely to cause false positives (specifically, it assumes roughly half of the bits have been lost to rounding, e.g. the square root of the machine epsilon).

  2. Relative comparison by default. Most of the crates in the wild seem to use a hybrid between relative and absolute comparison. This is bad for arbitrary numbers which may have any scale, and gives up a number of desirable properties of the floating point number system.

  3. Absolute comparison with zero. The only downside to using relative comparison by default is that it is essentially never useful to use relative comparison where one of the values is known in advance to be zero.

    As a result, this library provides almost::zero(v) as well, which uses absolute comparison.

  4. Properly handling both overflow and underflow.

    Because this library uses relative comparison, denormal numbers behave properly, as well as comparisons where one of the values has overflowed to infinity. The second might sound impossible, but we can just rescale both values, and compare with the same tolerance.

  5. Simple API. We don't expose other ways of comparing numbers, most of which are either dubious choices for non-niche use cases.

That said, there's no one size fits all here. Numerical robustness is full of tradeoffs, and while I believe the ones made by this library are good for most cases, they do not and cannot satisfy every possible case.

No runtime deps