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
proposal: math: add an IsAlmostEqual function #17675
Comments
Personally I think this this requires too much knowledge of the floating point format to use correctly, and experts who need this function can easily write it themselves. My opinion could change if the math library for some other language provides a similar function. In general I feel that this kind of function can be written in several different way, and which way to use requires understanding the algorithm you are trying to implement. That means that adding one such function to the standard library is an attractive nuisance for people who are not experts. |
While it requires some knowledge of floating-point to understand, I don't see it as hard to use. For example, to see if A and B (both float64's) are almost equal you would write:
I did look around about before settling on the implementation I proposed. Perhaps I didn't look hard enough, but this seemed to be what was used in the overwhelming number of cases I found. I agree that it can be misused, but that's true of lots of things. :) I would argue that the name of the function serves as a fairly strong warning that it isn't the same as "==". |
The suspect IsAlmostEqual is is almost never what you want. If you know what you are doing in a numerical algorithm and understand float math, the function most likely does too much or too little and you end up writing your own specialized code anyway. On the other hand, for people unsure about floating point properties, IsAlmostEqual may lure them into a false sense correctness. I'm not convinced this function carries its weight. |
When you say it might do too much or too little, I don't understand. Could you expand on this or perhaps give examples? I would argue that == is almost never what you want. Someone who doesn't understand is probably going to start with ==, then use a fixed delta (ex: 0.000001). By definition, he isn't going to understand enough to do it correctly. So he's stuck. Reference: Knuth, sec 4.2.2, page 217: A programmer using floating point arithmetic almost never wants to test if two computed values are exactly equal to each other (or at least he hardly ever should try to do so), because this is an extremely improbably occurrence. |
Agreed that == is usually wrong. But the proposed function does quite a bit of work, and I suspect ( I don't have proof) that more often than not it does too much (no need to take care of Inf or NaN), and as a consequence may be slower than necessary. It's also harder to analyze the numerical code's error behavior because one now has to understand the detailed properties of IsAlmostEqual. Testing that We have been reluctant to add functionality to math that's not essential, and this function doesn't seem to fall in that category either. |
I don't understand some of the points you are making. Does too much work: There is but a single Analyzing the properties: How does someone writing their own routine make this easier?
That's exactly what this function does. It's the last line. The two prior lines determine the right (relative) eps. Are you suggesting that there is a better of doing this? Not essential: I would argue that it's more essential than the floating point I would argue that it is an occassionally needed function. Since it's not obvious to many programmers how to implement it, they typically use a kludge involving a fixed eps - which is unusable in a library and is a landmine for refactoring. |
What I mean by "too much" is that floating-point algorithms that care about speed will want to do the absolute minimum - no need to deal with infinities or NaNs, and possible compute the epsilon beforehand (e.g. a loop) rather than each time. In my experience with floating-point arithmetic, conditions like this one are often required to determine termination conditions of loops. I am not disagreeing with you that this is an "occasionally needed" function - but so are many functions. That's not a sufficient criteria for inclusion in a standard library. Let's have other people chime in here; ideally folks with experience in numerical algorithms. I've simply stated my opinion, others may feel differently and may have concrete evidence to substantiate their claims. |
I see your point about calculating the eps once when there's a loop. Looking forward to seeing usage feedback. |
Expanding over this:
one should also note that |
Regardless of whether adding this to stdlib is a good idea or not, what is the reason for using the word Almost in IsAlmostEqual? I would expect the function to be called Equal and the signature of the function to define the expected conditions for equality. |
Almost is what differentiates it from strict equality. It allows for limited precision of floating point. |
func IsAlmostEqual(a, b float64, nrSteps int) (equal bool) { For me the "Almost" adverb removes clarity; the third parameter to the func should clarify the conditions under which a is equal to b. The purpose of nrSteps isn't clear because it requires me to know something floating point representation. In other languages, the IsAlmostEqual functions either have a relative difference parameter (instead of nrSteps) or carry an implementation-defined relative difference . The func name is the same, but semantics differ, and my first impression was "what is nrSteps and why is it an int?". |
nrSteps: OK, I agree that "steps" perhaps isn't a great name, especially because perhaps I didn't define it well enough. Thus I would change
to
If that doesn't seem clear enough, then "nrSteps" could be changed to something like "nrRelativeEpsilons". And I just noticed that I should have said that the window was 2 x "nrSteps" big, since it's double-sided. Understanding this setting does require some understanding. I don't think that the function header is a good place for a tutorial on floating point, however. To make it easier for the average user I did show the common setting (1 or 2 steps). This setting could be eliminated by locking it down (to 1 or 2), but that would limit the function's use. What might be best would be to make the setting optional. Perhaps 2 functions (one with and one without it), or kludge it by making it a variant (expecting just 0 or 1 instances). The setting is an positive integer because the only window sizes that make sense are integral multiples of the relative epsilon. You are dealing with mantissa differences of a few bits, and you can't have a fraction of a bit. |
If you would like the functionality, gonum has several different versions which use for testing depending on the algorithm. For float64: For []float64: |
Interesting! Thanks. |
It's too large and complex a question to provide a single solution. Sorry. |
Proposal
Add an IsAlmostEqual function to the math package.
Justification
Although equating floating-point numbers is generally a bad idea,
sometimes it is necessary. For example, an iterative square root
algorithm might need an equate to determine when to quit.
Possible Solution
Add something like this function to the math package.
The text was updated successfully, but these errors were encountered: