![]() |
![]() |
|
Is \(\frac{x}{25} \cdot 25\) equal to \(x\)? Of course it is! Unless you are using floating-point arithmetic. (Try it \(x=7\), then \(14\), then \(21\).)
We understand the reason: Floating point arithmetic uses a fixed number of digits to represent numbers, though the location of the decimal-point might float1. And if you have a number like ⅓, but only use (say) 17 digits, you can't represent that perfectly (in base-10); you're left with a number that is very very3 close to the number you really want — but not == to it!
When modeling using doubles (or f64s or f32s or floats etc.), keep these facts in mind:
This last point bears repeating: Do not compare doubles with ==! But then, how do we compare two floating-point numbers? There are several approaches, each with their own drawbacks:
You might (write your own function to) test if two numbers are within some fixed, absolute distance of each other (say, within 0.0000001) and if so you consider them equal. And the precise amount of tolerance would be an optional parameter to that function.
Drawback: the right tolerance can depend on whether you're comparing numbers in the billions (like national budgets), or numbers far less than one (like the size of microsopic objects).
For unit-testing, JUnit provides this as
assertEquals(double x, double y, double tolerance);
Java itself does not have an agreed-upon standard, non-error-throwing comparison (!).
Rust has
expect_float_absolute_eq
and it's unit-testing counterpart
assert_float_absolute_eq.
Alternately, you might test if two numbers are relatively close
(that is, their ratio is within 0.0000001 of 1).
Drawback: If the second number is 0.0f, this test returns false
(possibly not even accepting that 0.0 == 0.0 ?).
This version is not in a standard Java library (to my knowledge); Rust has expect_float_relative_eq and it's unit-testing counterpart assert_float_relative_eq.
My preferred method is comparing how many low digits (bits) differ, since this doesn't suffer either of the two previous weaknesses. It's only drawback is that the meaning of the third/tolerance argument isn't so intuitive. In fact, the Rust version doesn't just whether the numbers have ≤ k low bits that differ, but instead whether they “are no more than n steps away from each other”, which is how many floats can lie in between the two numbers. This is exponentially more fine grained, since n = 2k.
Rust has expect_f64_near and it's unit-testing counterpart assert_f64_near.
In order to use unit tests with assert_f46_near, you first need to install the external crate (library) “assert_float_eq”. It's easy to do this, in either of two ways:
At this point, you can call the function via its full-name, assert_float_eq::assert_f64_near(…,…). However, that's quite a mouthful! Instead, you can write a line at the top of your file: use assert_float_eq::assert_f64_near;. At this point, you can just use that function's first name, assert_f64_near(…,…).
This page licensed CC-BY 4.0 Ian Barland Page last generated | Please mail any suggestions (incl. typos, broken links) to ibarland ![]() |