Game Development Community

F32's... dont they need an epsilon to compare?

by Dumbledore · in Technical Issues · 06/05/2008 (5:41 am) · 6 replies

In mPoint.h:

inline bool Point3F::operator==(const Point3F& _test) const
{
      return ( x == _test.x ) && ( y == _test.y ) && ( z == _test.z );
}

How could this ever be useful? I thought floating point numbers are never accurate enough to do explicity comparison so instead you use what they call an epsilon to approximate two float's equality.

Thoughts? I need to know if I am right so that I can implement a proper float comparison that can be relied upon in my code.

#1
06/05/2008 (8:39 am)
Two ways that could be valid:

== is overloaded

or

the values x and _test.x are potentially copies of each other. That is, _test.x was set with x somewhere else, not calculated. Once any calculation is done though, you can no longer depend on == like you said.
#2
06/05/2008 (9:20 am)
Dumbledore -
excellent observation.

if some code is comparing two floats for strict equality,
it's very very likely that the programmer has thought about the problem wrong.

i think the same extends to comparing two Point3Fs.

i would encourage you not to use the "==" token at all,
and instead write something like "isNear", which has an epsilon.

an interesting thought experiment:
choose two random points along a line segment of length one;
what is the probability that the two points are the same ?
#3
06/05/2008 (9:37 am)
Well I'll add my trademark "it depends".

First off, you are totally correct. If you want to compare floats that are a result of calculations, you should be using an epsilon. That being said, though, I think that the operator implementation is correct. It does exactly what it says it will do, which is return true if they are exactly equal. I think that it is up to the implementor to correctly identify places that need an epsilon, and the code should reflect that.

If someone skims a bit of code which has a bunch of "==" being used, they will pass right over it. When you see something like Orion suggested, an "isNear", than that's a better hint about what is going on. So if the "==" operator was changed to use epsilon compares, my fear is that it would start causing epsilon errors in the long run. That's totally my opinion, though. Everyone has their own way of thinking about stuff like this.

On the plus side, that Point3F function used const properly ;)
#4
06/05/2008 (11:19 am)
Ok, here's a hypothetical situation where you'd want exactly ==, and it should still work. It's a bit contrived...

Imagine you have a gaggle of points. You run through all of them and select 2 points. The one with the greatest x, and the one with the greatest z. Then you want to test if they are the same point.


Really, the only way I could see it coming up is storage of a point and later comparison against things that could be that same point. Of course in a situation like this, to get to that point you'd have to essentially look it up rather than calculate it (because then == would no longer work). And if you're looking it up somehow, you could do equality comparisons against the underlying criteria.
#5
06/08/2008 (8:21 am)
Thanks for your input everyone.

Orion says:
Quote:
an interesting thought experiment:
choose two random points along a line segment of length one;
what is the probability that the two points are the same ?

If the random number generator is truly random then the probability is 1 over infinity.

Pat says:
Quote:
I think that it is up to the implementor to correctly identify places that need an epsilon, and the code should reflect that.

I agree heartily that == should do exactly as it is told, especially if adding an epsilon to the compare could ever at any time cause problems. Now that I have confirmed with you that operator== doesn't already use an epsilon, my question is, could adding an epsilon compare to operator== ever cause problems? If not, I think it would be more clear to overload operator== with an implicit epsilon compare. Here is my reasoning:

If you think about a complex number like pi and use it in a calculation, you will never use the entire number of pi. This is okay since pi to 31 decimal places is sufficient to work out the circumference of any circle that fits in the observable universe to a precision comparable to the size of a hydrogen atom. By rounding pi to 3.14 the margin of error that you introduce is less than 1 percent. By rounding pi to 5 decimal places the margin of error introduced is less than 0.0001 percent.

Can anyone name a time in 3d graphics programming that you would need a precision higher than 0.0001 percent? If you cannot than I would contend that using an epsilon that computes to 5 floating point decimal places will always suffice, and since it will always suffice, it might be intuitive to overload operator== with the epsilon comparison.

That said, I'll stick with a Point3F::epsilonEquals method since that is irrefutably clear.
#6
06/08/2008 (11:58 am)
Point3F does provide epsilon comparisons via the "equal" function. Likewise providing a method to check "isZero".

I'm not sure whether I'd like to see ==operator overloaded to do an epsilon comparison, at least when you see pointA.equal( pointB ); is at least explicit and leads you to believe there is more going on behind the scenes than pointA == pointB would.