# Visual Basic > Visual Basic 6 and Earlier >  Floating-Point Comparisons (Again)

## mms_

A while back *wqweto* showed me the correct way to do floating-point comparisons



> This is the proper way to deal with floating-point comparisons -- by using a Private Const EPSILON As Double = 0.000001 for the margin of rounding errors.
> 
> a = b becomes Abs(a - b) < EPSILON
> a <> b becomes Abs(a - b) > EPSILON
> a < 0 becomes a < -EPSILON
> a <= 0 becomes a < EPSILON
> a < b becomes a < b - EPSILON
> a <= b becomes a < b + EPSILON



I'm wondering if I can turn this around somewhat

say
a=1.25
b=1.249999999

Create a function say EpsilonRound 

Perform the function on b
b=EpsilonRound(b)

so that a EQUALS b

I'm thinking something along the lines of this


```
Private Sub Command2_Click()

    Dim a As Single
    Dim b As Single
    
    a = 1.25
    b = 1.249999

    b = myEpsilonRound(b)

    If a = b Then
        MsgBox "Equal"
    Else
        MsgBox "NOT Equal"
    End If
    
End Sub

Private Function myEpsilonRound(value As Single) As Single

    myEpsilonRound = Round(value + Sgn(value) * 0.0000001, 5)

End Function
```

This seems to work for the test value provided (or I'm fooling myself in thinking it is)

I cannot get anything to work for this case.
a = 0.00390625
b = 0.0039062499999

Am I chasing something that is not possible?

----------


## Arnoutdv

You specify a value of 5 for the rounding decimal place.
Your value A has more decimal places

----------


## mms_

I've been fooling with that

This seems to work
1/512=0.001953125
which is the most decimal place accuracy I think I will need

I want this to work for any number say 
1/4=0.25


```
Private Function myEpsilonRound2(value As Single) As Single

    myEpsilonRound2 = Round(value + Sgn(value) * 0.0000000001, 9)

End Function
```

----------


## mms_

I THINK this works.

Doing it this way, I think I can do floating-point value comparisons the traditional (more intuitive) way
ie testing if a = b

If anyone wants to chime-in and say your fooling yourself, I would *appreciate hearing that*.

----------


## Elroy

mms, personally, I'd never trust an EpsilonRound function, because with IEEE Single or Double, we always have to remember that we're doing base-2 floating point under the hood, and that almost never exactly corresponds to the base-10 floating point that we see when we print (or type in) our numbers.  It's just very easy to have slight differences in the "tiny bits", which is why you need the Epsilon for comparisons in the first place.

----------


## Elroy

As another alternative, you could use the Decimal type (which requires use of Variants, and CDec function).

The Decimal type is a true base-10 storage method, and, under the hood, it's all integers with a base-10 exponent multiple.  Therefore, you don't need to use an epsilon for comparison with those.  The biggest downside is that you won't be using the Floating-Point-Processor to do your math when you use Decimals.  And also, the exponent multiple doesn't have near the range of either Single or Double (but it does have a nice range).  And the mantissa (significand) precision is more than either Single or Double.

----------


## Shaggy Hiker

I would say that what you are chasing isn't worth chasing. We work in decimal, computers work in binary. There are numbers that can't be displayed with perfect precision in decimal (1/3, 2/3, pi, etc.) and there are different numbers that can't be displayed with perfect precision in binary. Even when we work with paper and pencil we use conventions that add a bit of inaccuracy to our numbers, such as 2/3 being shown as 0.67 or 0.667, or whatever level of precision we choose to take it to. The same can be said with 3.1, 3.14, 3.1416, and so on.  

Computers are doing the same thing and for the same reason: We have to, and so do they. You can't 'fix' this in a way that is going to be universally correct. For example, suppose that you had a third of a pie and added a second third of a pie. In decimal, you would be adding 0.33 to 0.33, which could give you 0.66, which you might write as 0.67 because you know that it isn't JUST 0.66, but 0.6666666....etc. If we wrote it out the way we learn in grade school, it would be 1/3 + 1/3, which would be precisely 2/3, we just can't write that in decimal. 

So, if you were to try to 'fix' this, how would you do it? 0.66 is not correct for adding two thirds together. It's too low. 0.67 is not correct for adding two thirds together. It's too high. However, if you got to 0.66 by adding 0.33 and 0.33 together, rather than adding one third and one third, then 0.66 is THE correct answer. Do you want to compare that to two thirds? Sometimes it should be smaller (if you were adding 0.33 and 0.33, or adding 0.1 and 0.26) and other times it should be equal (if you were adding one third and one third, or adding one sixth and one half). So whether equality is even right depends on what the question is.

Therefore, the quest is hopeless. Any solution you find will have a flaw that will make it unusable in some contexts, and you won't be able to predict when it will work and when it won't.

----------


## mms_

OK thanks

I will go back to the original way that *wqweto* showed me.

to test if
*a = b* 
instead test if
*Abs(a - b) < EPSILON*

----------


## vb6forever

IMHO none of the above ways is right or wrong to compare floating point


```
a = b
```

The question that one should be asking is:

What degree of precision does our decision point (what happens after) rest on the values being compared.

Does it need to be an exact comparison or can be live with some wiggle room (some user defined value of EPSILON).   Based on what we want to happen after will determine that value of EPSILON.

----------


## Elroy

> I would say that what you are chasing isn't worth chasing. We work in decimal, computers work in binary. There are numbers that can't be displayed with perfect precision in decimal (1/3, 2/3, pi, etc.) and there are different numbers that can't be displayed with perfect precision in binary. Even when we work with paper and pencil we use conventions that add a bit of inaccuracy to our numbers, such as 2/3 being shown as 0.67 or 0.667, or whatever level of precision we choose to take it to. The same can be said with 3.1, 3.14, 3.1416, and so on.  
> 
> Computers are doing the same thing and for the same reason: We have to, and so do they. You can't 'fix' this in a way that is going to be universally correct. For example, suppose that you had a third of a pie and added a second third of a pie. In decimal, you would be adding 0.33 to 0.33, which could give you 0.66, which you might write as 0.67 because you know that it isn't JUST 0.66, but 0.6666666....etc. If we wrote it out the way we learn in grade school, it would be 1/3 + 1/3, which would be precisely 2/3, we just can't write that in decimal. 
> 
> So, if you were to try to 'fix' this, how would you do it? 0.66 is not correct for adding two thirds together. It's too low. 0.67 is not correct for adding two thirds together. It's too high. However, if you got to 0.66 by adding 0.33 and 0.33 together, rather than adding one third and one third, then 0.66 is THE correct answer. Do you want to compare that to two thirds? Sometimes it should be smaller (if you were adding 0.33 and 0.33, or adding 0.1 and 0.26) and other times it should be equal (if you were adding one third and one third, or adding one sixth and one half). So whether equality is even right depends on what the question is.
> 
> Therefore, the quest is hopeless. Any solution you find will have a flaw that will make it unusable in some contexts, and you won't be able to predict when it will work and when it won't.


Everything Shaggy said is correct, but it's even worse than that.  There are numbers (specifically numbers with a fraction part) that can be exactly displayed in base-10 but can't be represented in base-2 (and vice-versa).  And more times than not, when we multiply and/or divide two Doubles (or Singles), we're getting great base-2 precision (down in the tiny-bits) that just don't "play well" when thinking of them as base-10.  In fact, it gets so bad that you can even find separate IEEE bit-patterns (which is what "if a=b" will use) that will print out as the exact same base-10 number (even when showing full precision).  In other words, when using IEEE Single or Double, you've just got to: Abs(a-b) > epsilon

----------


## georgekar

I use this code for my M2000 Interpreter

This is the code as executed in M2000. Using from 0 to 6 you get true, from 7 false

This rounding routine preserve the type of variant.



```
a = 0.00390625
b = 0.0039062499999
? round(a,6)=round(b,6)
```




```
Static Function MyRound(a As Variant, Optional ByVal i As Integer = 0)
Dim j As VbVarType
j = VarType(a)
If j < vbSingle Or j = 20 Then MyRound = a: Exit Function
Dim c As Variant
Static n(1 To 28) As Single, D(1 To 28) As Double, cur(1 To 28) As Currency, dec(1 To 28) As Variant
Static SG(-1 To 1) As Single, sg4(-1 To 1) As Double, sg8(-1 To 1) As Currency
On Error GoTo there
If n(1) = 0 Then
    SG(-1) = CSng(-0.5)
    SG(1) = CSng(0.5)
    sg4(-1) = -0.5
    sg4(1) = 0.5
    sg8(-1) = CCur(-0.5)
    sg8(1) = CCur(0.5)
    For c = 1& To 6&
        n(c) = CSng(10 ^ c)
    Next c
    For c = 7& To 27&
        n(c) = CSng(-1)
    Next c
        n(c) = CSng(10 ^ 5)
        n(10) = CSng(10 ^ 5)
    For c = 1& To 13&
        D(c) = CDbl(10 ^ c)
    Next c
    For c = 14& To 27&
        D(c) = CDbl(-1)
    Next c
    D(c) = CDbl(10 ^ 13)
    
    For c = 1& To 3&
        cur(c) = CCur(10 ^ c)
    Next c
    For c = 4& To 28&
        cur(c) = CCur(-1)
    Next c
    For c = 1& To 28&
        dec(c) = CDec(10 ^ c)
    Next c
End If
    If i = 0 Then
        MyRound = Sgn(a) * Int(Abs(a) + 0.5!)
        Exit Function
    Else
        c = Fix(a)
        Select Case j
        Case vbSingle
            If n(i) > 0 Then
                c = SG(Sgn(a))
                MyRound = Fix(a * n(i) + c) / n(i)
            Else
                MyRound = a
            End If
            Exit Function
        Case vbDouble
            c = sg4(Sgn(a))
 
            If D(i) > 0 Then
                MyRound = Fix(a * D(i) + c) / D(i)
            Else
                MyRound = a
            End If
            Exit Function
        Case vbCurrency
            If cur(i) > 0 Then
                c = sg8(Sgn(a))
                MyRound = Fix(a) + Fix((a - Fix(a)) * cur(i) + c) / cur(i)
            Else
                MyRound = a
            End If
            Exit Function
        Case vbDecimal
            c = sg8(Sgn(a))
            MyRound = Fix(a) + Fix((a - Fix(a)) * dec(i) + c) / dec(i)
            Exit Function
        Case Else
            MyRound = a
            Exit Function
        End Select
    End If
there:
Err.Clear
MyRound = a
End Function
```

----------


## ntstatic

> IMHO none of the above ways is right or wrong to compare floating point
> 
> 
> ```
> a = b
> ```
> 
> The question that one should be asking is:
> 
> ...



yes this is the right method i have been doing this for a long time 

if abs(a-b)<0.00001 then msgbox "Equal" else msgbox "not equal"

for a precision of 5 places (or 4, calculate which ). i just give it a couple of decimal places for good measure for eg if i am calculating weight of an ornament i use 5 decimal instead of the usual 3 for a milligram precision

----------


## Elroy

You know?  To do this "better", you could pull the mantissa (significand) out of the IEEE numbers, and directly compare them with an epsilon.  That would allow you to compare two numbers regardless of where their decimal place was.  To do that, we'd have to watch out for infinities, NaNs, and subnormals, but that wouldn't be any huge deal.  Also, we'd have to make decisions about whether any NaN is equal to any other NaN, but those are good decisions to think about.

When I get finished with my LongLong work, maybe I'll tackle this.

----------


## Elroy

Here's some code I threw together (just in a Form1):



```

Option Explicit
'
Private Type Holding
    lLo  As Long
    lHi  As Long
End Type
'
Private Declare Function GetMem8 Lib "msvbvm60" (ByRef Source As Any, ByRef Dest As Any) As Long ' Always ignore the returned value, it's useless.
'


Private Sub Form_Load()
    Dim d1 As Double
    Dim d2 As Double

    ' SOME TESTING

    Debug.Print " ******** "; Rnd; " just a separator"
    d1 = 0#: d2 = 1#:                                       Debug.Print d1 = d2, DblSame(d1, d2)     ' False    False
    d1 = 0#: d2 = 1#:                                       Debug.Print d1 = d2, DblSame(d1, d2)     ' False    False
    d1 = 0#: d2 = 0.00000001:                               Debug.Print d1 = d2, DblSame(d1, d2)     ' False    False
    d1 = 0#: d2 = 9.88131291682493E-324:                    Debug.Print d1 = d2, DblSame(d1, d2)     ' False    True
    d1 = 1.79769313486232E+307: d2 = 1.79769313486233E+307: Debug.Print d1 = d2, DblSame(d1, d2)     ' False    False
    d1 = 1# / 3#: d2 = 2# / 3# / 2#:                        Debug.Print d1 = d2, DblSame(d1, d2)     ' True     True
    d1 = 5.444 * 3.123: d2 = 2.722 * 1.041 * 6#:            Debug.Print d1 = d2, DblSame(d1, d2)     ' False    True

End Sub


Public Function DblSame(d1 As Double, d2 As Double, Optional bbTinyBitsToIgnore As Byte = &H7&) As Boolean
    ' bbTinyBitsToIgnore is a MASK for the mantissa.  As such, all bits to the right (smaller) than the
    ' highest-order-bit that's on should also be on.  For instance, the following would be valid
    ' and reasonable values:
    '       &h1     ignore one bit.
    '       &h3     ignore two bits.
    '       &h7     ignore three bits.
    '       &hf     ignore four bits.
    '       &h1f    ignore five bits.
    '       &h3f    ignore six bits.
    '       &h7f    ignore seven bits.
    '       &hff    ignore eight bits.  This is starting to get extreme, and is the most allowed.
    ' Subnormal shouldn't matter as that's still down in the tiny bits.
    '
    If IsInf(d1) And IsInf(d2) Then DblSame = True  ' We'll let all infinities match.
    If IsNaN(d1) And IsNaN(d2) Then DblSame = True  ' We'll let all NaNs match.
    '
    Static u1 As Holding        ' Will use for mantissa of d1.
    Static u2 As Holding        ' Will use for mantissa of d2.
    GetMem8 d1, u1
    GetMem8 d2, u2
    ' The following u1.lHi <> u2.lHi tests several things:
    '   If sign bits don't match, they're not the same.
    '   If exponents don't match, they're not the same.
    '   If high 20 bits of the mantissa don't match, they're not the same.
    If u1.lHi <> u2.lHi Then Exit Function
    '
    ' And now we decide how much of the mantissa to ignore, and compare.
    Static lMask As Long: lMask = Not CLng(bbTinyBitsToIgnore)
    DblSame = (u1.lLo And lMask) = (u2.lLo And lMask)
End Function

Private Function IsNaN(d As Double) As Boolean
    IsNaN = IsNanOrInf(d) And Not IsInf(d)
End Function

Private Function IsInf(d As Double) As Boolean
    Const ii As Integer = &H7FF0    ' High 4 bits of byte #7 (F0), Low 7 bits of byte #8 (7F). If all on, it's NaN (or Inf if all other non-sign bits are zero).
    Static i(3) As Integer
    GetMem8 d, i(0)
    IsInf = (i(3) And ii) = ii And i(0) = &H0 And i(1) = &H0 And i(2) = &H0 And (i(3) And &HF) = &H0
End Function

Private Function IsNanOrInf(d As Double) As Boolean
    Const ii As Integer = &H7FF0    ' High 4 bits of byte #7 (F0), Low 7 bits of byte #8 (7F). If all on, it's NaN (or Inf if all other non-sign bits are zero).
    Static i(3) As Integer
    GetMem8 d, i(0)
    IsNanOrInf = (i(3) And ii) = ii
End Function


```

Rather than insisting that you decide how many decimal places you want your precision to, it allows you to just trim a bit of the precision from the actually IEEE number before the comparison.  That way, you can still compare very large and/or very small numbers for "almost identical" values.

I think I'm still of the opinion though that, if we know how much decimal point precision we want (i.e., how much of the fractional portion), the Abs(a - b) > epsilon is probably still better.

Just as some explanation, an IEEE Double (which is what VB6 is using) has 52 mantissa bits (53 counting the one that's implicit).  And it's down in the least-significant-bits of this mantissa where we run into trouble with a=b comparisons.  Therefore, the above just masks those off (forcing them to match) before the comparison is done.  Doing it this way, the actual magnitude (very large and/or very small) doesn't matter (other than the exponents must match exactly).

----------

