`Double.Equals` help

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.

https://forum.xojo.com/t/double-equals-zero/49061

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 0.2 (which Round would).

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
    End Function

or

return round(d * pow(10,decimalPoints)) = round(v * pow(10,decimalPoints))

for faster results

2 Likes

if d.Equals(0 + 0.000000000000001,1) then
//pad out your value to ensure they have the same depth
end if

@Graham_Busch Your 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
    Return True
  Else
    Return False
  End If
End Function

It’s faster than my current implementation and I’ve added it to my (growing) MathsKit Xojo library.

Thank you.

1 Like

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.

4 Likes

Thanks for that.

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.

1 Like