I have succeeded in writing an Ulps based function that compares two doubles for equality. According to this page, the comparison can be made using a combination of absolute and relative epsilon or using integers (Ulps).
I have made both epsilon based and Ulps based functions. This is the epsilon based function:
var IsAlmostEqual_Epsilon = function(a, b) { if (a == b) return true; var diff = Math.abs(a - b); if (diff < 4.94065645841247E-320) return true; a = Math.abs(a); b = Math.abs(b); var smallest = (b < a) ? b : a; return diff < smallest * 1e-12; }
And this is the Ulps based (DoubleToInt64Bits
, subtract
, negate
and lessthan
functions are in the below mentioned JSBIN):
var IsAlmostEqual_Ulps = function(A, B) { if (A==B) return true; DoubleToInt64Bits(A, aInt); if(aInt.hi < 0) aInt = subtract(Int64_MinValue, aInt); DoubleToInt64Bits(B, bInt); if(bInt.hi < 0) bInt = subtract(Int64_MinValue, bInt); var sub = subtract(aInt, bInt); if (sub.hi < 0) sub = negate(sub); if (lessthan(sub, maxUlps)) return true; return false; }
According to Bruce Dawson the Ulps based is preferred. IsAlmostEqual_Ulps
is working ok according to test base of 83 cases, but the function is pretty slow. It takes about 700-900 ms to complete the test base (JSBIN) when executed as a standalone html (outside JSBIN). Epsilon based IsAlmostEqual_Epsilon
takes only about 100 ms.
Is there anything that can be done to speedup IsAlmostEqual_Ulps
function? You can propose also a completely different solution or some fixings to my code.
I have tested already the inlining everything, but it cuts the time only about 5-10%. I'm hunting something like 50-80% improvement in execution time. 100-500% improvement would be fine, but it may be only a dream.
right_answers
in the JSBIN code are got using C# IsAlmostEqual
function (see at the top of JSBIN code). Both above functions give the same results in all 83 cases.
EDIT:
C++ version from here:
bool IsAlmostEqual(double A, double B)
{
//http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
long long aInt = reinterpret_cast<long long&>(A);
if (aInt < 0) aInt = -9223372036854775808LL - aInt;
long long bInt = reinterpret_cast<long long&>(B);
if (bInt < 0) bInt = -9223372036854775808LL - bInt;
return (std::abs(aInt - bInt) <= 10000);
}