This question is related to this, but because I try to optimize the function for speed, I ask this question as a new one.
I'm translating new floats Clipper library from Angus Johnson to Javascript, and there is a function IsAlmostEqual
, which compares doubles for equality using Ulps technique.
The original C# function is here:
public static bool IsAlmostEqual(double A, double B) { //http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm const int maxUlps = 10000; Int64 aInt = BitConverter.DoubleToInt64Bits(A); if (aInt < 0) aInt = Int64.MinValue - aInt; Int64 bInt = BitConverter.DoubleToInt64Bits(B); if (bInt < 0) bInt = Int64.MinValue - bInt; Int64 sub = unchecked(aInt - bInt); if (sub > aInt != bInt < 0) return false; return (sub <= 0 && sub > -maxUlps) || (sub > 0 && sub < maxUlps); }
And my translation of it (in JSBIN):
var IsAlmostEqual_Ulps = function(A, B) { 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; }
My Javascript version seems to work OK according to 83 sample values, but the problem is the slowness, which mainly comes from DoubleToInt64Bits
function. When the code is executed as a standalone html (outside JSBIN), the total run time is about 2267 ms, of which DoubleToInt64Bits
takes 983 ms.
The problematic (=slow) DoubleToInt64Bits
is here:
function DoubleToInt64Bits(A, xInt) { (new Float64Array(buf))[0] = A; xInt.lo = (new Uint32Array(buf))[0] | 0; xInt.hi = (new Int32Array(buf))[1] | 0; }
Is there any way to make DoubleToInt64Bits
faster?
A DataView
is a more appropriate class to read out different types from a buffer rather than creating different typed arrays on the buffer. Here is your DoubleToInt64Bits
method rewritten to use a DataView
.
function DoubleToInt64Bits(A, xInt)
{
var dataView = new DataView(buf);
dataView.setFloat64(0, A);
xInt.lo = dataView.getUint32(4) | 0;
xInt.hi = dataView.getInt32(0) | 0;
}
This cuts my run time from ~1500ms to ~850ms.