Comparing Floating Point Numbers

What I’m going to discuss isn’t anything specific to Inventor but is a general issue to be aware of when working with computers and floating point numbers. The thing you need to understand is that floating point numbers are not always exact.  They’re very very close to the expected number and close enough for computations but you can run into problems when comparing numbers.  Below is a VBA sample that illustrates the problem.  I want to emphasize that the issue is not limited to VBA but can be seen with any programming language.

Public Sub CompareNumbers() 
    Dim val1 As Double 
    val1 = 0.1 + 0.1 + 0.1

    Dim val2 As Double 
    val2 = 0.3

    Debug.Print "val1 = " & CStr(val1) & ", val2 = " & CStr(val2)

    If val1 = val2 Then 
        Debug.Print "Values match"
    Else
        Debug.Print "Values do not match"
    End If

    Debug.Print "Difference = " & val1 – val2
End Sub

When I run the program above I get the following results in the Immediate window.

val1 = 0.3, val2 = 0.3
Values do not match
Difference = 5.55111512312578E-17

The first line is what you would expect but the second and third lines are a bit of a surprise.  You would expect the two variables to have the same value but the comparison shows that they are not equal and the last line shows by how much they are different; 0.0000000000000000555111512.  That value is close enough to being equal that in almost all cases you don’t need to care about the difference but when using the “=” comparison they must match exactly.

More than you ever wanted to know about why this happens can be found at http://en.wikipedia.org/wiki/Floating_point.  A simpler explanation can be found here: http://floating-point-gui.de/ 

The Solution
The solution to this problem is to not ever expect floating point numbers to be exactly equal but instead allow for them to be equal within a tolerance.  This is described in one of the links I provided above (http://floating-point-gui.de/errors/comparison/).  Here’s my simple solution which according to the link is not the right way to do it, but I think in my case it works because I know the range of values that I’m working with.  Anyone working with Inventor is limited to sizes and tolerances that Inventor can handle so we know we’re not working with sizes in light years or nanometers.  When comparing two points, Inventor considers them to be the same if they’re within 0.0000000001 centimeters.

The code below is a modified version of the previous Sub that uses a function to do the value comparison.

Public Sub CompareNumbers2()
    Dim val1 As Double
    val1 = 0.1 + 0.1 + 0.1

    Dim val2 As Double
    val2 = 0.3

    Debug.Print "val1 = " & CStr(val1) & ", val2 = " & CStr(val2)

    If IsEqual(val1, val2) Then
        Debug.Print "Values match"
    Else
        Debug.Print "Values do not match"
    End If
End Sub

Below is the IsEqual function that compares to the two values within the specified tolerance.

Public Function IsEqual(Value1 As Double, Value2 As Double, _
            Optional Tolerance As Double = 0.000000000001) As Boolean
    If Abs(Value1 – Value2) < Tolerance Then
        IsEqual = True
    Else
        IsEqual = False
    End If
End Function

Below is a more elaborate version of the IsEqual function that supports various types of comparisons.

' Compares two numbers to see if they meet the comparison condition
' within the specified tolerance.
' Valid comparison operators are, "=" "<", ">", "<=", and ">=".
Public Function NumbersCompare(Value1 As Double, Value2 As Double, _
           operator As String, _
           Optional Tolerance As Double = 0.000000000001) As Boolean
    Dim difference As Double
    difference = Abs(Value1 – Value2)
   
    Dim comparison As String
    comparison = ""
    Dim equals As Boolean
    equals = False
    If Len(operator) = 2 Then
        ' Determine if the operator is greater or less than.
        If Mid(operator, 1, 1) = ">" Then
            comparison = ">"
        ElseIf Mid(operator, 1, 1) = "<" Then
            comparison = "<"
        Else
            MsgBox "Bad comparison operator: " & operator
            NumbersCompare = False
            Exit Function
        End If
       
        ' Determine if equals to is specified.
        If Mid(operator, 2, 1) = "=" Then
            equals = True
        Else
            MsgBox "Bad comparison operator: " & operator
            NumbersCompare = False
            Exit Function
        End If
    ElseIf Len(operator) = 1 Then
        ' Determine if the operator is greater than,
        ' less than, or equals to.
        If operator = ">" Then
            comparison = ">"
        ElseIf operator = "<" Then
            comparison = "<"
        ElseIf operator = "=" Then
            equals = True
        Else
            MsgBox "Bad comparison operator: " & operator
            NumbersCompare = False
            Exit Function
        End If
    Else
        MsgBox "Bad comparison operator: " & comparison
        NumbersCompare = False
        Exit Function
    End If
   
    ' Do the actual comparisons.
    If operator = ">" Then
        ' Check if the value is greater than, using the tolerance.
        If Value1 + Tolerance > Value2 Then
            NumbersCompare = True
            Exit Function
        Else
            NumbersCompare = False
        End If
    ElseIf operator = "<" Then
        ' Check if the value is less than, using the tolerance.
        If Value1 – Tolerance < Value2 Then
            NumbersCompare = True
            Exit Function
        Else
            NumbersCompare = False
        End If
    End If

    ' Check if the numbers are equal, within the tolerance.
    If equals Then
        If difference <= Tolerance Then
            NumbersCompare = True
            Exit Function
        Else
            NumbersCompare = False
        End If
    End If
End Function

Here’s an example of the function above in use.

Public Sub CompareNumbers3()
    Dim val1 As Double
    val1 = 0.1 + 0.1 + 0.1

    Dim val2 As Double
    val2 = 0.3

    Debug.Print "val1 = " & CStr(val1) & ", val2 = " & CStr(val2)

    If NumbersCompare(val1, val2, "<=") Then
        Debug.Print "Values compare"
    Else
        Debug.Print "Values do not compare"
    End If
End Sub

This is only a problem when working with floating point numbers.  Integers and strings are always exact and exact comparisons will work as expected.

-Brian


Comments

4 responses to “Comparing Floating Point Numbers”

  1. Amoolya Deeven Nuthalapati Avatar
    Amoolya Deeven Nuthalapati

    Very usefull !!!
    Thanks Indeed…

  2. Amoolya Deeven Nuthalapati Avatar
    Amoolya Deeven Nuthalapati

    Brian..
    Can how do we compare AlphaNumeric Values??
    If we have an array Like
    100A
    900
    1200
    1000
    1235
    2040
    we expect array to sort out like
    100A
    900
    1000
    1200
    1235
    2040
    How to get this sorting?? can we use same logic as you showed in your blog??

  3. What I discussed in this post doesn’t apply to your specific issue. Floating point accuracy is a completely different issue.
    What’s happening in your case is that you need to sort a series of strings in a particular way. I recommend doing a search on sorting algorithms. Depending on what your application is you might also be able to take advantage of sorting capabilities built into some of the Microsoft controls.

  4. Amoolya Deeven Nuthalapati Avatar
    Amoolya Deeven Nuthalapati

    Thanks Brian for the reply.
    I got my head around floating point concept when writing export scripts for production.
    And with sorting, I found Quick sort and bubble sort as quick and easy to use.
    But, when sorting, how to compare alphanumeric values(they have to be declared as variant)???
    For i = lngMin To lngMax – 1
    For j = i + 1 To lngMax
    If arr(i)) > arr(j)) Then
    strTemp = arr(i)
    arr(i) = arr(j)
    arr(j) = strTemp
    End If
    Next j
    Next i
    any objects like Cstr,If IsNumeric are there to compare??
    It wil be very helpful if we got around this.

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading