-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
relative_eq() has incorrect behavior for small values #65
Comments
Rust follow IEEE 754 all is specified here. (but I have no idea if this will answer you) |
@dsroche does the code below illustrate your point? I'm not that familiar with relative floating point comparisons: fn relative_eq(lhs: f32, rhs: f32, epsilon: f32, max_relative: f32) -> bool {
// Handle same infinities
if lhs == rhs {
return true;
}
// Handle remaining infinities
if f32::is_infinite(lhs) || f32::is_infinite(rhs) {
return false;
}
let abs_diff = f32::abs(lhs - rhs);
// For when the numbers are really close together
if abs_diff <= epsilon {
return true;
}
let abs_lhs = f32::abs(lhs);
let abs_rhs = f32::abs(rhs);
let largest = if abs_rhs > abs_lhs { abs_rhs } else { abs_lhs };
println!("This part is never reached");
// Use a relative difference comparison
abs_diff <= largest * max_relative
}
fn main() {
let diff = 1.0e-7;
let x: f32 = 1.0e-5;
let y: f32 = x - diff;
println!(
"relative_eq!(x, y) = {}",
relative_eq(x, y, f32::EPSILON, f32::EPSILON) // true
);
println!(
"abs_diff <= largest * max_relative = {}",
(x - y).abs() <= 1.0e-5 * f32::EPSILON, // false
);
} So one has to pass their own epsilon in order for it to work for small values? E.g: use approx::relative_eq;
relative_eq!(x, y, epsilon = 1.0e-8f32) |
Oooh thanks for the report! I may have got something wrong in the implementation - it's been a long time since I looked inside. And to be honest floating point numerics are not something I'm an expert on. I'd be interested if we can find a fix for this though! |
Consider this small example:
Note that
x
andy
are normal, full-precision floating-point values (not "subnormals"), above theepsilon
threshold, and their relative error is (x - y)/x, which equals 0.01, far above the default threshold. However, they are marked as "relatively equal" in the current implementation.The problem is in the logic of the provided implementation of
RelativeEq::relative_eq()
forf32
andf64
, namely these lines:approx/src/relative_eq.rs
Lines 65 to 70 in 308176d
While the precise definition of relative equality and the parameters
epsilon
andmax_relative
are never clearly specified in the documentation, based on the referenced blog posts, it seems that the intention is to useepsilon
as a zero-closeness threshold, andmax_relative
as the relative error threshold.The issue stems from not checking the two input numbers (
self
andother
in this case) individually againstepsilon
for a zero-threshold, but instead testing their absolute difference againstepsilon
. As a result, relative equality works as expected for large numbers, but in practice defaults to absolute-difference equality for values less than 1.The text was updated successfully, but these errors were encountered: