@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
@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 ?
I figured something must be broken here.
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
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
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