# `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

`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
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