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
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.
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.
How about using
if Round(d) = 0 then
(I know this isn’t using Double.Equals, but…)
It needs more precision that that I’m afraid. For example,
0.0000001 should be considered
0 but not
I guess it’s getting into slowing things down again, but Round(d*1000) or whatever would do it.
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
return round(d * pow(10,decimalPoints)) = round(v * pow(10,decimalPoints))
for faster results
if d.Equals(0 + 0.000000000000001,1) then
//pad out your value to ensure they have the same depth
IsCloseTo method is pretty good - thank you. I’m now using it in this method to compare with zero:
Protected Function IsZero(d As Double) as Boolean
If Round(d * Pow(10, 6)) = 0 Then
It’s faster than my current implementation and I’ve added it to my (growing) MathsKit Xojo library.
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
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
This gives a relative comparison (i.e., percentage) rather than absolute.