Can someone help me use Double.Equals correctly please? I’ve read the docs but I’m still not clear.

I need the following statement to be true:

d = 0 // where d = 0.000000000000001

This homegrown method works:

If Abs(0 - d) <= Tolerance Then // Tolerance = 0.000000000000001
Return True
Else
Return False
End If

Whilst this works it requires a call to Abs and a call to a computed property Tolerance. In a tight loop this has performance issues.

I should be able to use:

If d.Equals(0.0, x) Then // x is MaxUIps from the docs
// d is zero.
End If

No matter what value I use for x in the Equals method it always returns False. Can someone help explain UIps more clearly than the docs. Specifically, how do I make 0.000000000000001 = 0.0?

If it may interest you I have already opened a similar post some time ago. I’m still not convinced about the answers but apparently there is no solution.

I use this function to check if a double is close enough

Public Function IsCloseTo(extends d as double, v as double, decimalPoints as integer = 5) as boolean
if d = v then return true
dim d1,v1,r as integer
r = pow(10,decimalPoints)
d1 = round(d * r)
v1 = round(v * r)
return d1 = v1
End Function

I was curious about what exactly is going on with this method because I wasn’t aware it existed. So maybe I can shed a little light on what’s happening.

Take this code:

Var Mem As New MemoryBlock(16)
Mem.LittleEndian = False
Mem.DoubleValue(0) = 3.1415926535897932
Mem.DoubleValue(8) = 3.141592653589795

The bytes at offset 7 and 15 are what are getting compared. As the documentation lists, that would be 0x18 (24) and 0x1C (28). This is why using maxUIps of 3 is not equal but maxUIps of 4 is equal.

In your original example, the last byte is 0x16 (22) and 0’s is of course 0. So you’d need a maxUlps value of 22 or greater to successfully compare the two.

In theory. In practice…

Var Zero As Double = 0
Var Equals As Boolean = Zero.Equals(0.000000000000001, 22)

Equals is still false. So… I don’t know. Even if I was correct, I can’t think of how such a function would be useful. Maybe it doesn’t work with zeros?

It strikes me that this is surely a bug in Double.Equals or, like you say @Thom_McGrath it’s an oversight when it comes to comparing with zero. Maybe Xojo needs to add a Double.EqualsZero extension…

maxUlps functions on the underlying binary representation of a double and specifies how many bits can be different. It is well known, however, that ulps comparison breaks down at zero.

0.000000000000001 is &h3CD203AF9EE75616
but 0.0 is a special case: &h0000000000000000

They’re not even close. No amount of maxUlps is going to work.

A lot of people seem to think that the smallest value that can be represented by a double is 0.000000000000001, when in fact the smallest value is roughly 1.0e-300, which is many many orders of magnitude smaller. This could explain why many of the custom comparison functions don’t always behave as expected. This is especially problematic if you need to compare values that have the exponent part set to non zero values. For my scientific work where I may be dealing with numbers with a large exponent part I use this function:

Public Function NearlyEqual(n1 as double, n2 as double, nSigDigits as integer) as Boolean
'Return true if numbers are in agreement to the specified number of significant figures
return abs(1-n1/n2)<10^-nSigDigits
End Function

This gives a relative comparison (i.e., percentage) rather than absolute.