Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparison of two floats in Rust to arbitrary level of precision

Tags:

rust

How can I do a comparison at an arbitrary level of precision such that I can see that two numbers are the same? In Python, I would use a function like round(), so I am looking for something equivalent in Rust.

For example I have:

let x = 1.45555454;
let y = 1.45556766;

In my case, they are similar up to 2 decimal places. So x and y would become 1.46 for the purposes of comparison. I could format these, but that surely is slow, what is the best Rust method to check equivalence, so:

if x == y { // called when we match to 2 decimal places}

To further elucidate the problem and give some context. This is really for dollars and cents accuracy. So normally in python would use the round() function with all its problems. Yes I am aware of the limitations of floating point representations. There are two functions that compute amounts, I compute in dollars and need to handle the cents part to the nearest penny.

The reason to ask the community is that I suspect that if I roll my own, it could hit performance and it's this aspect - which is I why I'm employing Rust, so here I am. Plus I saw something called round() in the Rust documentation, but it seems to take zero parameters unlike pythons version.

like image 375
disruptive Avatar asked Jan 03 '17 16:01

disruptive


People also ask

How do you compare two floating point values?

To compare two floating point values, we have to consider the precision in to the comparison. For example, if two numbers are 3.1428 and 3.1415, then they are same up to the precision 0.01, but after that, like 0.001 they are not same.

How much precision does a float have?

float is a 32-bit IEEE 754 single precision Floating Point Number – 1 bit for the sign, 8 bits for the exponent, and 23* for the value. float has 7 decimal digits of precision.

Why is it a problem to compare two floating point numbers?

Comparing for equality Floating point math is not exact. Simple values like 0.1 cannot be precisely represented using binary floating point numbers, and the limited precision of floating point numbers means that slight changes in the order of operations or the precision of intermediates can change the result.

Why do floats lose precision?

Floating-point decimal values generally do not have an exact binary representation. This is a side effect of how the CPU represents floating point data. For this reason, you may experience some loss of precision, and some floating-point operations may produce unexpected results.


1 Answers

From the Python documentation:

Note The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float.

For more information, check out What Every Programmer Should Know About Floating-Point Arithmetic.


If you don't understand how computers treat floating points, don't use this code. If you know what trouble you are getting yourself into:

fn approx_equal(a: f64, b: f64, decimal_places: u8) -> bool {
    let factor = 10.0f64.powi(decimal_places as i32);
    let a = (a * factor).trunc();
    let b = (b * factor).trunc();
    a == b
}

fn main() {
    assert!( approx_equal(1.234, 1.235, 1));
    assert!( approx_equal(1.234, 1.235, 2));
    assert!(!approx_equal(1.234, 1.235, 3));
}

A non-exhaustive list of things that are known (or likely) to be broken with this code:

  • Sufficiently large floating point numbers and/or number of decimal points
  • Denormalized numbers
  • NaN
  • Infinities
  • Values near zero (approx_equal(0.09, -0.09, 1))

A potential alternative is to use either a fixed-point or arbitrary-precision type, either of which are going to be slower but more logically consistent to the majority of humans.

like image 70
Shepmaster Avatar answered Oct 04 '22 15:10

Shepmaster