Double Equals

@Garry - try this

Dim d As Double

d = 0 // where d = 0.000000000000001


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

Break

That doesn’t work if you set d to the tiny value that he wants equal to 0.

Sure it does

MaxUIPS is … well … document weird to say the least

Dim d As Double

d = 0 // where d = 0.000000000000001


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

Break

this usage would permit .000000000000001 to .000000000000006 to be considered “equal”

maxUIPS is the amoubt between the last digits

Dim d As Double

d = 0.000000000000001


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

Break

since I set the “equals” call to permit a range of 0.000000000000001 to 0.000000000000002 (1 difference of 1 in the right most digit) is restricts equalness to a tiny range

I understand it should work, but does your example with .00_01 and .00_03 actually work for you? It doesn’t for me. 2020r1 on Mac.

.000_01 and .000_03 should NOT work - thats the point
The call to Equals says “treat .000_03 as equal to d IF the very last digit is within 1” and .0003 ISNT
Its 2 away from .00_001
Alter that code to use 2 and it will break treating them as if they are equal

Its why I say the docs are enormously unclear on how it works

Should this work? (edit: should it break as if they are equal?)

Dim d As Double = 0.01
dim d2 as Double = 0.02

If d.Equals(d2 ,1 ) Then // x is MaxUIps from the docs
  // equal
  Break
End If

Break

I dont believe so and this is why I say “the docs suck”

maxUIPS isnt really “the right most digit” - its “the right most byte” as far as I understand" - and not a “tolerance” like what would probably be better and more useful generally

For maxUIps, the last position refers to the the last byte in the binary representation of the mantissa. maxUIps is the amount of difference between the last byte of the 2 numbers that is still acceptable.

The link on the page for Equals is a good read on how this is supposed to work and it is not intuitive at all
It really limits you to the last byte which is always very very very tiny differences since its the last 8 bits of the mantissa

Yeah, I read the link and get that it’s the last byte, but didn’t see why it wasn’t working for numbers below 1. I should have read the other thread linked to learn why. Also to read a workaround/solution by some Norman Palardy guy. LOL

This is too complicated.

This isn’t working:

Var d As Double = 0.0000000000000001
If d.Equals(0.0, 1) Then Break // Never breaks
If d.Equals(0.0, 2) Then Break // Never breaks
If d.Equals(0.0, 3) Then Break // Never breaks
If d.Equals(0.0, 4) Then Break // Never breaks
If d.Equals(0.0, 5) Then Break // Never breaks
If d.Equals(0.0, 6) Then Break // Never breaks

I’ve always thought how this worked should have been more a “tolerance” but thats not whats is there
Its close but not intuitive at all

You almost have to look at what the bytes are for the double by dumping them into a memory block and examining them

Dim mb As New memoryblock(8)
mb.LittleEndian = False
mb.DoubleValue(0) = 0.0000000000000001
break

that gives a result of

3c 9c d2 b2 97 d8 89 bc

odd but ok
so if the tolerance is 1 then that _should permit values from

3c 9c d2 b2 97 d8 89 bb
to
3c 9c d2 b2 97 d8 89 bd

to be “equal”

but the debugger in Xojo says

3c 9c d2 b2 97 d8 89 bb

is 0.0000000000000001 ???
and

3c 9c d2 b2 97 d8 89 bd

is … is 0.0000000000000001 ???

so it seems “something” is broken

however since a double CAN be dumped into a memory block and bytes compared Equals could probably be made to work using our own Xojo code
A “CloseEnoughWithTolerance” method that allows you to specify a tolerance that is as precise as a double allows seems plausible but there will be inaccuracies because doubles have limited accuracy (but huge range)

Or you just use Bob Delaneys plugins and get infinite precision ?

1 Like

I figured something must be broken here. :angry:

I took a look at Bob’s plugin page - some really good stuff on there. I think I want to stick to using Doubles rather than his Decimal type as this is for a physics engine that I want to run on iOS.

So far what I’m doing is first computing the machine epsilon of the system the engine is running on and caching it:

// Computes an approximation of machine epsilon.
Var e As Double = 0.5

While (1.0 + e > 1.0)
  e = e * 0.5
Wend

Return e

This gives me (on my current machine) a value of 0.0000000000000001.

I have a computed property (Tolerance) that returns episilon + 0.00000001. This gives me my tolerance above zero I’m happy with (0.0000000100000001 in this case).

I then have the following method to test if a Double should be considered zero:

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

This works. Annoyingly I only have to do this because Xojo’s built-in Sign method doesn’t work for tiny tiny Doubles (it returns 1 for 0.000000000000001).

I’m just paranoid about performance as this function is called repeatedly in collision detection algorithms (and other areas) so gets hammered a lot.

So after a little bit more testing, I’ve settled on a suggestion by Graham Busch on the Xojo Inc forum and modified my method to the following:

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

Seems reasonably performant. The 6 is the number of decimal places of accuracy.

You could rewrite Sign for double since the IEEE 754 FP format is well defined
Plop the double in and MB and a few bit ANDS and ORS and off you go

I love how easily binary mathematics comes to you Norm :smile:

But sign returns -1 for negative, 0 for 0, 1 for positive
So for a tiny value like that it should say “positive”
No ?

Correct although I guess it would be nice if there was a Sign method that also accepted a “tolerance” value.

Well we could write one :slight_smile:

here’s the sign function


Static mb As memoryblock

If mb Is Nil Then
  mb = New memoryblock(8)
  mb.LittleEndian = False
End If

// returns -1 For negative, +1 For positive (fwiw 0 is "positive in IEEE754 formats)
mb.DoubleValue(0) = d

If ((mb.Byte(0) And &h7F) = 0) And ((mb.Byte(1) And &hFF) = 0) And ((mb.Byte(2) And &hFF) = 0) And ((mb.Byte(3) And &hFF) = 0) _
  And ((mb.Byte(4) And &hFF) = 0) And ((mb.Byte(5) And &hFF) = 0) And ((mb.Byte(6) And &hFF) = 0)  And ((mb.Byte(7) And &hFF) = 0) then
  return 0
Elseif (mb.Byte(0) And &h80) = 0 Then
  Return 1
Else 
  Return -1
End If
1 Like