-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Add RelativeEq trait #6296
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
Add RelativeEq trait #6296
Conversation
@Erik-S alerted me to this problem today on IRC. If this is an inherent problem with |
Yeah, let's try to stop proliferating |
All right I'll try merge these two. |
I have two options in my mind. i) changing approx_eq_eps() like this
ii) add methods for relative error checking something like this
Thoughts? |
In my opinion, these two properties should hold: approx_eq(1e-12,2e-12) = false approx_eq(1e15,1e15+1) = true The first is false because those numbers are "far apart" (one is twice the other). The second is true because they are "close" - 2 units in the least significant bit, if I did my math right (and assuming f64). The second property shouldn't cause much trouble. The first could cause trouble if there is subtractive cancellation, which can lead to large relative errors. But if an algorithm has catastrophic subtractive cancellation, the algorithm is broken (usually). Like most of floating point math, there is no one right answer. We need to provide a reasonable solution, and then people can code their own algorithms if they need more. |
Because I think just one method couldn't be a solution for all kind of problem. |
What are your thoughts @pcwalton, seeing as you use ApproxEq in rust_geom? |
Part of my dislike for the relative_eq() - style math (not scaling eps by the input magnitude) is that there is literally no value of eps that will be reasonable across even a small part of the range of floating point numbers (roughly 1e-300 to 1e300 - 600 orders of magnitude). The approx_eq() (scaling eps by magnitude) still doesn't have one right value for eps, but you can define a reasonable value over the whole range. We could provide a version of relative_eq() that takes eps as an argument, but that starts getting complicated. This would be a great place to use default values on arguments, if we could. |
@bjz @Erik-S Thanks for your comments. |
I have concerns about the duplicated computations in your gist. @pcwalton and I are going to be using these for matrix and vector comparisons, so efficiency is important. Would this be slightly better? fn approx_eq_eps(&self, other: &f32, absolute_epsilon: &f32, relative_epsilon: &f32) -> bool {
match (*self - *other).abs() {
// check absolute error
n if n < *absolute_epsilon => true,
// check relative error
n => n < self.abs().max(&other.abs()) * (*relative_epsilon),
}
}
} You'd also have to rename |
i like the function that takes epsilon as a parameter - you'd want to rely on the compiler inlining constants |
@ILyoan That looks pretty good. I'd recommend deleting absolute_epsilon() (since the default isn't used, and will rarely be correct). I'd recommend setting eps to: |
@Erik-S could we just do this to be self-documenting?: #[inline(always)] fn approx_epsilon() -> f32 { 64f32 * Float::epsilon() }
#[inline(always)] fn approx_epsilon() -> f64 { 64f64 * Float::epsilon() } It would hopefully be inlined and evaluated at compile time by the compiler (wishing for associated constants here...). Also, why the choice of 64? Is that arbitrary? |
Rustup r? `@ghost` changelog: none
Fix for #5316
Current Implementation of ApproxEq trait uses absolute error method, and this method is working well for small float point numbers but not suitable for large numbers.
This patch provides RelativeEq trait and implementation of it uses relative error method that will be better for comparing big numbers.