Double equals zero

Hi, there,

I was doing a series of calculations with my application when I realized a very strange thing… the double equals method doesn’t work with numbers close to zero.

If I have such a structure:

dim prova as Double = -1.0000000000000001
dim prova2 as Double =1

if prova.equals(prova2,1) then
break
end if 

if prova2.equals(prova*-1,1) then
break 'ok
end if

in debug stops correctly in the second if because the appearance between the two numbers is right (equal to 1). If instead I have a structure of this type:

dim prova as Double = -0.0000000000000001
dim prova2 as Double =0

if prova.equals(prova2,1) then
break
end if 

if prova2.equals(prova*-1,1) then
break
end if

here does not stop in either of the if… I thought it was a problem of signs in front of the -0 for this I came to multiply by -1, but this thing is absurd…Ideas?

While I’m at it, I’ll ask you one more question. I opened a feedback for another problem a few weeks ago but for now has not been taken into account.With what logic are processed?
In addition, if I look in the various lists of feedback available I no longer see it … <https://xojo.com/issue/55877>

From https://documentation.xojo.com/api/data_types/double.html

[quote]Notes
Double is an IEEE double-precision, floating-point value. This means it is speedy but has some limits for values it can contain. For more information, refer to the wikipedia page about floating point or the Floating Point Primer blog post.[/quote]

Yes ok, that’s the reason why you can not replicate the exact decimals in floating point, not why the equals you use to overcome this difference does not work.

My guess is that you need to change your maxUIps value. You need to change the last byte value difference, it may not be 1 as you expect.

https://documentation.xojo.com/api/code_execution/equals.html

When comparing doubles for equality, the typical pattern is to take the absolute value of the difference between the two doubles and return TRUE if it’s smaller than some epsilon value (e.g. 0.00000001).

That being said, if you care about doubles being equal, make sure you can afford those small differences. If you can’t (e.g. you’re doing currency math) use the appropriate fixed-point data type like Currency.

[quote=442233:@Alberto De Poo]My guess is that you need to change your maxUIps value. You need to change the last byte value difference, it may not be 1 as you expect.

https://documentation.xojo.com/api/code_execution/equals.html[/quote]

In the above case, what value of maxUlps would you recommend?

[quote=442236:@Philippe Casgrain]When comparing doubles for equality, the typical pattern is to take the absolute value of the difference between the two doubles and return TRUE if it’s smaller than some epsilon value (e.g. 0.00000001).

That being said, if you care about doubles being equal, make sure you can afford those small differences. If you can’t (e.g. you’re doing currency math) use the appropriate fixed-point data type like Currency.[/quote]

Thank you, now I try this approach, even though I don’t have a fixed epsilon value to consider as an error spread. The calculations in question do not relate to currencies, and in fact I can also admit a much higher spread that is not influential. The problem, however, is to understand what is happening otherwise the software loops.

Attilio, I’m no expert just learning. I’ve been trying to find a value but I can’t.

I don’t understand why, if the docs say:

[quote]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. For example, consider these 2 numbers:

3.1415926535897932 // last byte value is 0x18 3.141592653589795 // last byte value is 0x1C
A maxUIps of 3 will result in “not equal”, while a maxUIps of 4 will result in “equal”.[/quote]
and reading that the mantissa is only the decimal part, why that works, and this:

2.1415926535897932 2.141592653589795
works, but if I change the number to 1. or 0. it doesn’t work

From what I understand from https://documentation.xojo.com/api/code_execution/equals.html

This code should always work:

[code]Dim prova As Double = 3.1415926535897932 // last byte value is 0x18
Dim prova2 As Double =3.141592653589795 // last byte value is 0x1C

If prova.equals(prova2,4) Then
MsgBox “works”
Else
MsgBox “doesn’t work”
End If

prova = 2.1415926535897932
prova2 =2.141592653589795

If prova.equals(prova2,4) Then
MsgBox “works”
Else
MsgBox “doesn’t work”
End If

prova = 1.1415926535897932
prova2 =1.141592653589795

If prova.equals(prova2,4) Then
MsgBox “works”
Else
MsgBox “doesn’t work”
End If

prova = 0.1415926535897932
prova2 =0.141592653589795

If prova.equals(prova2,4) Then
MsgBox “works”
Else
MsgBox “doesn’t work”
End If [/code]

but only the first 2 work as expected and the last 2 doesn’t.

Alberto,
Don’t worry, I’m trying to understand the problem too and that’s why I opened the post. Unfortunately I don’t think it’s a documentation problem, but it will probably be an equals bug with numbers starting with 0.
Thank you so much for your help. I’ll try to do some more tests to understand each other more

[quote=442247:@Alberto De Poo]From what I understand from https://documentation.xojo.com/api/code_execution/equals.html
but only the first 2 work as expected and the last 2 doesn’t.[/quote]

prova = -1.1415926535897932
prova2 =-1.141592653589795 

If prova.equals(prova2,4) Then
MsgBox "works"
Else
MsgBox "doesn't work"
End If 


prova = -2.1415926535897932
prova2 =-2.141592653589795 

If prova.equals(prova2,4) Then
MsgBox "works"
Else
MsgBox "doesn't work"
End If 

Again, the first one does not work and the second one does not. In practice, in the range [1,-1] including extremes does not work.

Note that the maxUIps value refers to the binary (or hex) representation. With that in mind, look at the hex representation of the numbers converted to the IEEE floating point representation:

3.1415926535897932 = 0x400921FB54442D18
3.141592653589795 = 0x400921FB54442D1C
Difference in last bytes (1C - 18) is 4
For maxUIps of 4: Equal but for maxUIps of 3: Not Equal

2.1415926535897932 = 0x400121FB54442D18
2.141592653589795 = 0x400121FB54442D1C
Difference in last bytes (1C - 18) is 4
For maxUIps of 4: Equal but for maxUIps of 3: Not Equal

1.1415926535897932 = 0x3FF243F6A8885A30
1.141592653589795 = 0x3FF243F6A8885A38
Difference in last bytes (38 - 30) is 8
For maxUIps of 4: Not Equal

0.1415926535897932 = 0x3FC21FB54442D183
0.141592653589795 = 0x3FC21FB54442D1C4
Difference in last bytes (C4 - 83) is 41
For maxUIps of 4: Not Equal

Google IEEE Floating Point converters to do your own conversions.

You’ll notice that the difference in the last bytes increases as you get closer to zero. The author of the blog post referenced in the Xojo Equals documentation states that “The ULPs based technique also breaks down near zero”. The post is well worth reading.

The same author also notes that “You just won’t believe how vastly, hugely, mind-bogglingly hard it [floating point math] is.”

Thank you Dennis, now I understand how Equal works. The key is: the technique breaks down near zero. For example:

Dim a As Double = 0.001000000000000001 Dim b As Double = 0.001000000000000057
the last byte for a is 01 and the last byte for b is 03, but maxUIps needs to be 258 to make a.equals(b,258) true and that’s because the last 2 bytes are AA01 for a and AB03 for b.

That’s because the leading zeros don’t count. The mantissa ignores “.00” and begins with “1000000000000001”. But then it has more bits to use, so it keeps approximating and the difference becomes greater.

Thank you all for your support. I’ve finally figured out how it works. Unfortunately, however, I still remain a little dubious about the actual use of this method … Correct me if I’m wrong, I can use this method only if I establish a domain of values before. If I do operations on non-established numbers how do I know before how long the value of maxUIps should be ? Being a lazy programmer I would have expected such considerations to be made at the Double class level as a native type. So the only practical solution really applicable is the one recommended by @Philippe Casgrain.

[quote=442280:@Dennis Hoskins]
You’ll notice that the difference in the last bytes increases as you get closer to zero. The author of the blog post referenced in the Xojo Equals documentation states that “The ULPs based technique also breaks down near zero”. The post is well worth reading.[/quote]

I have read the post reported above and it is really interesting, but as mentioned above, explains why it is difficult to represent the various numbers in floating point but has nothing to do with equals function.

Depending on what it is you’re trying to use floating point FOR it may or many not be useful
The problem domain is relevant

If youre trying to use this for monetary calculations you might be better off with a “fixed point” representation (an int64 with a fixed number of decimals) and then you just reformat things in the input & output

Currency may work for you but there are still issues with them that may or may not affect you
<https://xojo.com/issue/20841>
<https://xojo.com/issue/55610>
<https://xojo.com/issue/52218>
<https://xojo.com/issue/48725>
<https://xojo.com/issue/39894>

[quote=442402:@Norman Palardy]Depending on what it is you’re trying to use floating point FOR it may or many not be useful
The problem domain is relevant

If youre trying to use this for monetary calculations you might be better off with a “fixed point” representation (an int64 with a fixed number of decimals) and then you just reformat things in the input & output

Currency may work for you but there are still issues with them that may or may not affect you
<https://xojo.com/issue/20841>
<https://xojo.com/issue/55610>
<https://xojo.com/issue/52218>
<https://xojo.com/issue/48725>
<https://xojo.com/issue/39894>[/quote]

Thanks for the answer. I will keep your suggestion good for other cases. For now it seems that I have solved my problem in another way.