I implemented the AlmostEqual2sComplement as proposed by Bruce Dawson, but for comparing double instead of float values.
Similar implementations can be found in many places.
bool AlmostEqual2sComplement(double A, double B, int maxUlps= 10)
{
// Make sure maxUlps is non-negative and small enough that the
// default NAN won't compare as equal to anything.
// assert maxUlps > 0 && maxUlps < 4 * 1024 * 1024;
long long aInt = *(long long*)&A;
// Make aInt lexicographically ordered as a twos-complement int
if (aInt < 0)
aInt = 0x8000000000000000 - aInt;
// Make bInt lexicographically ordered as a twos-complement int
long long bInt = *(long long*)&B;
if (bInt < 0)
bInt = 0x8000000000000000 - bInt;
long long intDiff = aInt - bInt;
if (intDiff < 0)
intDiff= intDiff*-1;
if (intDiff <= maxUlps)
return true;
return false;
}
Unfortunately when comparing the double value -1.0 and 4.0 the function returns true.
This is because the intDiff results in this case to be equal 0x8000000000000000 and
the absolute value of 0x8000000000000000 is again 0x8000000000000000
My current solution to this problem is not to take the absolute value of intDiff but rather change the comparision of intDiff and maxUlps to:
if (intDiff <= maxUlps && -maxUlps <= intDiff)
return true;
There must be more (maybe not so evident) cases where intDiff results in 0x8000000000000000.
I’m wondering if other implementations of AlmostEqual2sComplement are just not aware of this problem, or if I made a mistake in my original implementation?
Here http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition is the corrected version, where there is an additional check whether the two floats have opposite sign.