[Qt-interest] strange qFuzzyCompare implementation
Stephan Rose
kermos at somrek.net
Mon Apr 13 02:05:04 CEST 2009
On Sun, 2009-04-12 at 18:42 +0200, Thiago Macieira wrote:
> Peter Radagast wrote:
> >Hello!
> >I think all of us got used to high quality of the Qt design and
> > implementation. And I was very disappointed, when I saw the
> > qFuzzyCompare implementation:
>
> That's because you didn't understand the purpose behind those functions.
>
> >static inline bool qFuzzyCompare(double p1, double p2)
> >{
> > return (qAbs(p1 - p2) <= 0.000000000001 * qMin(qAbs(p1), qAbs(p2)));
> >}
> >
> >static inline bool qFuzzyCompare(float p1, float p2)
> >{
> > return (qAbs(p1 - p2) <= 0.00001f * qMin(qAbs(p1), qAbs(p2)));
> >}
> >
> >Do you consider these hard-coded numbers etc effective, precise and
> > portable?
>
> Yes. They work fine for the intent that these functions are meant to have.
>
> > I supposed to see smth like:
> >static inline bool qFuzzyCompare(double p1, double p2)
> >{
> > return (qAbs(p1 - p2) <= std::numeric_limits::epsilon<double>());
> >}
>
> That would have the opposite effect of what is wanted. And it would not
> work.
>
> For one thing, no two numbers can have a difference that is smaller than
> epsilon / order of magnitude. They can either be equal (exactly), differ by
> one epsilon/order of magnitude or more. Remember that epsilon is the
> smallest number that, added to 1, makes it different from 1. But if you had
> 1e-9 instead of 1, you the difference would be epsilon * 1e-9. Even if you
> took the order of magnitude into consideration in your function, it would
> compare only if the two numbers are exactly equal or differ by the smallest
> unit that they could differ by.
>
> Like I said, that's not the intent of the function. The intent of the
> function is to verify if the two numbers are sufficiently close that they
> should be considered equal for the code in question. The problem is that
> the many floating point operations accumulate rounding errors: the more
> operations you make, the greater that error is. It's definitely greater
> than one epsilon.
>
> So your code would definitely not work.
>
> We could have chosen to make the code compare to a multiple of epsilons
> (like 1000 epsilon), but instead we chose to make our lives easier and
> just write a very small number.
There actually is another way to do a float compare like this that does
*not* rely on that type of hardcoded value.
#define MaxError 5
bool compareFloat(float p1, float p2)
{
int a, b;
memcpy(&a, &p1, sizeof(float));
memcpy(&b, &p2, sizeof(float));
a = a < 0 ? 0x80000000 - a : a;
b = b < 0 ? 0x80000000 - b : b;
return qabs(a - b) <= MaxError;
}
This basically will do the same as what you were looking to do,
*without* relying on a value like 0.0001 or equivalent. It exploits the
fact that if you copy the float data into an integer and subtract b from
a, the result will be how close the two values are to one another,
regardless of their magnitude.
If there is no error, a-b = 0.
The greater the error is the larger a-b will be. You simply set MaxError
to what you want the greatest deviation in your code to be.
Small numbers will have a very small margin of error, really large
numbers will have a correspondingly larger margin of error.
Of course, you have to make sure that on the platform that sizeof(int)
== sizeof(float) and if not, use a different type that matches. Same
can be done with doubles as well, just use a 64-bit int instead.
Stephan
More information about the Qt-interest-old
mailing list