Home All Groups Group Topic Archive Search About

Problem comparing double values

Author
5 Jul 2005 11:43 AM
Chris Eden
Hello all, I have a strange problem that I would appreciate any help
with. I have an application that involves a comparison of two numbers,
one of which is made up of the sum of the values in an array. The code
is as follows:

Private Sub CompareNumbers()
  Dim intIndex As Integer
  Dim arNumbers(5) As Double
  Dim dblTotal As Double
  Dim dblComparison As Double

  arNumbers(0) = 16843.2
  arNumbers(1) = 40348.41
  arNumbers(2) = 371910.18
  arNumbers(3) = 148183.27
  arNumbers(4) = 29183.96
  arNumbers(5) = 143530.98

  For intIndex = 0 To 5
    dblTotal = dblTotal + arNumbers(intIndex)
  Next

  dblComparison = 750000
  If dblComparison = dblTotal Then
    MsgBox "Array Total:" & Chr$(9) & dblTotal & vbCr & "Comparison:" &
Chr$(9) & dblComparison & vbCr & "Numbers match"
  Else
    MsgBox "Array Total:" & Chr$(9) & dblTotal & vbCr & "Comparison:" &
Chr$(9) & dblComparison & vbCr & "No match"
  End If
End Sub

Notice the oddity? The numbers are apparently identical and yet
according to the code, they do not match. What is even more bizzare is
that if I reduce the total of the numbers in the array to 597000, made
up of the following:
''  arNumbers(0) = 57843.2
''  arNumbers(1) = 44348.41
''  arNumbers(2) = 371910.18
''  arNumbers(3) = 50183.27
''  arNumbers(4) = 29183.96
''  arNumbers(5) = 43530.98
the comparison suceeded.

I don't think I have ever come up against anything so strange or so
frustrating; if anyone can shed any light on this it would be greatly
appreciated.

Thanks in advance

Chris

*** Sent via Developersdex http://www.developersdex.com ***

Author
5 Jul 2005 12:03 PM
Tony Proctor
Floating point is not an exact numeric representation Chris. I'm afraid the
newsgroups are full of questions like this. I don't have a link to hand that
would explain all the why's and where's, but maybe someone else will post
one.

From the values you have, it looks like they're all monetary values. If so
then try changing all your 'Double' types to 'Currency'. That is an exact
numeric representation. To be extra safe, I'd probably stick an '@'
character at the end of any numeric literals (e.g. 16843.2@) to make sure
that VB compiles them correctly

        Tony Proctor

Show quoteHide quote
"Chris Eden" <ec2***@hotmail.com> wrote in message
news:#9934aVgFHA.3936@TK2MSFTNGP10.phx.gbl...
> Hello all, I have a strange problem that I would appreciate any help
> with. I have an application that involves a comparison of two numbers,
> one of which is made up of the sum of the values in an array. The code
> is as follows:
>
> Private Sub CompareNumbers()
>   Dim intIndex As Integer
>   Dim arNumbers(5) As Double
>   Dim dblTotal As Double
>   Dim dblComparison As Double
>
>   arNumbers(0) = 16843.2
>   arNumbers(1) = 40348.41
>   arNumbers(2) = 371910.18
>   arNumbers(3) = 148183.27
>   arNumbers(4) = 29183.96
>   arNumbers(5) = 143530.98
>
>   For intIndex = 0 To 5
>     dblTotal = dblTotal + arNumbers(intIndex)
>   Next
>
>   dblComparison = 750000
>   If dblComparison = dblTotal Then
>     MsgBox "Array Total:" & Chr$(9) & dblTotal & vbCr & "Comparison:" &
> Chr$(9) & dblComparison & vbCr & "Numbers match"
>   Else
>     MsgBox "Array Total:" & Chr$(9) & dblTotal & vbCr & "Comparison:" &
> Chr$(9) & dblComparison & vbCr & "No match"
>   End If
> End Sub
>
> Notice the oddity? The numbers are apparently identical and yet
> according to the code, they do not match. What is even more bizzare is
> that if I reduce the total of the numbers in the array to 597000, made
> up of the following:
> ''  arNumbers(0) = 57843.2
> ''  arNumbers(1) = 44348.41
> ''  arNumbers(2) = 371910.18
> ''  arNumbers(3) = 50183.27
> ''  arNumbers(4) = 29183.96
> ''  arNumbers(5) = 43530.98
> the comparison suceeded.
>
> I don't think I have ever come up against anything so strange or so
> frustrating; if anyone can shed any light on this it would be greatly
> appreciated.
>
> Thanks in advance
>
> Chris
>
> *** Sent via Developersdex http://www.developersdex.com ***
Author
5 Jul 2005 1:50 PM
Rick Rothstein
> Floating point is not an exact numeric representation Chris. I'm afraid the
> newsgroups are full of questions like this. I don't have a link to hand that
> would explain all the why's and where's, but maybe someone else will post
> one.

INFO: Visual Basic and Arithmetic Precision
http://support.microsoft.com/default.aspx?scid=http://support.microsoft..com:80/support/kb/articles/Q279/7/55.ASP&NoWebContent=1

(Complete) Tutorial to Understand IEEE Floating-Point Errors
http://support.microsoft.com/default.aspx?scid=http://support.microsoft..com:80/support/kb/articles/Q42/9/80.ASP&NoWebContent=1

Rick - MVP
Author
5 Jul 2005 2:51 PM
Tony Proctor
Thanks Rick!  ...check in the post... <g>

        Tony Proctor

"Rick Rothstein" <rickNOSPAMnews@NOSPAMcomcast.net> wrote in message
news:ewDgHiWgFHA.2180@TK2MSFTNGP15.phx.gbl...
> Floating point is not an exact numeric representation Chris. I'm afraid
the
> newsgroups are full of questions like this. I don't have a link to hand
that
> would explain all the why's and where's, but maybe someone else will post
> one.

INFO: Visual Basic and Arithmetic Precision
http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/Q279/7/55.ASP&NoWebContent=1

(Complete) Tutorial to Understand IEEE Floating-Point Errors
http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/Q42/9/80.ASP&NoWebContent=1

Rick - MVP
Author
5 Jul 2005 3:33 PM
Duane Bozarth
Tony Proctor wrote:
>
> Floating point is not an exact numeric representation Chris. I'm afraid the
> newsgroups are full of questions like this. I don't have a link to hand that
> would explain all the why's and where's, but maybe someone else will post
> one.
....

My favorite is What Every Computer Scientist Should Know About Floating
Point a reprint of the original of which can be found at

http://docs.sun.com/source/806-3568/ncg_goldberg.html

There are some on MSN as well....
Author
5 Jul 2005 12:04 PM
Al Reid
Show quote Hide quote
"Chris Eden" <ec2***@hotmail.com> wrote in message news:%239934aVgFHA.3936@TK2MSFTNGP10.phx.gbl...
> Hello all, I have a strange problem that I would appreciate any help
> with. I have an application that involves a comparison of two numbers,
> one of which is made up of the sum of the values in an array. The code
> is as follows:
>
> Private Sub CompareNumbers()
>   Dim intIndex As Integer
>   Dim arNumbers(5) As Double
>   Dim dblTotal As Double
>   Dim dblComparison As Double
>
>   arNumbers(0) = 16843.2
>   arNumbers(1) = 40348.41
>   arNumbers(2) = 371910.18
>   arNumbers(3) = 148183.27
>   arNumbers(4) = 29183.96
>   arNumbers(5) = 143530.98
>
>   For intIndex = 0 To 5
>     dblTotal = dblTotal + arNumbers(intIndex)
>   Next
>
>   dblComparison = 750000
>   If dblComparison = dblTotal Then
>     MsgBox "Array Total:" & Chr$(9) & dblTotal & vbCr & "Comparison:" &
> Chr$(9) & dblComparison & vbCr & "Numbers match"
>   Else
>     MsgBox "Array Total:" & Chr$(9) & dblTotal & vbCr & "Comparison:" &
> Chr$(9) & dblComparison & vbCr & "No match"
>   End If
> End Sub
>
> Notice the oddity? The numbers are apparently identical and yet
> according to the code, they do not match. What is even more bizzare is
> that if I reduce the total of the numbers in the array to 597000, made
> up of the following:
> ''  arNumbers(0) = 57843.2
> ''  arNumbers(1) = 44348.41
> ''  arNumbers(2) = 371910.18
> ''  arNumbers(3) = 50183.27
> ''  arNumbers(4) = 29183.96
> ''  arNumbers(5) = 43530.98
> the comparison suceeded.
>
> I don't think I have ever come up against anything so strange or so
> frustrating; if anyone can shed any light on this it would be greatly
> appreciated.
>
> Thanks in advance
>
> Chris
>
> *** Sent via Developersdex http://www.developersdex.com ***

The problem is that they are not the same due to inherent rounding errors and the internal representation of floating point numbers.

?dblComparison-dblTotal
1.16415321826935E-10

The following will work.  It uses variants and the decimal subtype.

Private CompareNumbers()
  Dim intIndex As Integer
  Dim arNumbers(5) As Double
  Dim dblTotal As Variant
  Dim dblComparison As Variant

  arNumbers(0) = CDec(16843.2)
  arNumbers(1) = CDec(40348.41)
  arNumbers(2) = CDec(371910.18)
  arNumbers(3) = CDec(148183.27)
  arNumbers(4) = CDec(29183.96)
  arNumbers(5) = CDec(143530.98)

  For intIndex = 0 To 5
    dblTotal = dblTotal + arNumbers(intIndex)
  Next

  dblComparison = CDec(750000)
  If dblComparison = dblTotal Then
    MsgBox "Array Total:" & Chr$(9) & dblTotal & vbCr & "Comparison:" & _
Chr$(9) & dblComparison & vbCr & "Numbers match"
  Else
    MsgBox "Array Total:" & Chr$(9) & dblTotal & vbCr & "Comparison:" & _
Chr$(9) & dblComparison & vbCr & "No match"
  End If
End Sub

--

Al Reid
Author
5 Jul 2005 2:03 PM
Rick Rothstein
Show quote Hide quote
> Hello all, I have a strange problem that I would appreciate any help
> with. I have an application that involves a comparison of two numbers,
> one of which is made up of the sum of the values in an array. The code
> is as follows:
>
> Private Sub CompareNumbers()
>   Dim intIndex As Integer
>   Dim arNumbers(5) As Double
>   Dim dblTotal As Double
>   Dim dblComparison As Double
>
>       ......<code snipped>......
>
> Notice the oddity? The numbers are apparently identical and yet
> according to the code, they do not match.

If you can go with the Currency or Decimal data type solutions, that would probably be the way to go. However, for completeness sake, if you want to work with numbers of type Double, the best way to test equality is to choose some value such that if the difference between the two floating point numbers is less than it, you would be willing to call the numbers equal. For example

Dim A As Double
Dim B As Double
' A and B get their values from some set of calculations
If Abs(A - B) < 1e-10 Then
   ' Close enough to be considered equal
End If

Here I've chosen 1e-16 which is 0.0000000001 but you can chose any value suitable for your needs and the data type of your variables.

Rick - MVP
Author
6 Jul 2005 12:38 PM
Pásztor, Zoltán
Rick Rothstein wrote:
Show quoteHide quote
>> Hello all, I have a strange problem that I would appreciate any help
>> with. I have an application that involves a comparison of two
>> numbers, one of which is made up of the sum of the values in an
>> array. The code is as follows:
>>
>> Private Sub CompareNumbers()
>>   Dim intIndex As Integer
>>   Dim arNumbers(5) As Double
>>   Dim dblTotal As Double
>>   Dim dblComparison As Double
>>
>>       ......<code snipped>......
>>
>> Notice the oddity? The numbers are apparently identical and yet
>> according to the code, they do not match.
>
> If you can go with the Currency or Decimal data type solutions, that
> would probably be the way to go. However, for completeness sake, if
> you want to work with numbers of type Double, the best way to test
> equality is to choose some value such that if the difference between
> the two floating point numbers is less than it, you would be willing
> to call the numbers equal. For example
>
> Dim A As Double
> Dim B As Double
> ' A and B get their values from some set of calculations
> If Abs(A - B) < 1e-10 Then
>    ' Close enough to be considered equal
> End If
>
> Here I've chosen 1e-16 which is 0.0000000001 but you can chose any
> value suitable for your needs and the data type of your variables.
>
> Rick - MVP

Hi Rick,

In most cases using the 'epsilon' tolerance won't cause any problem.
Theoretically, however, it isn't quite correct - you break the transitivity
of the equality relation. Example (untested):

   Function NearlyEqual(ByVal A As Double, ByVal B As Double) As Boolean
      NearlyEqual = Abs(A - B) < 1e-5
   End Function

   Private Sub TestThem()
     Const cUB As Long = 10
     Dim  aTemp(0 To cUB)   As Double
     Dim  i As Long
     Dim dW As Double
     Const  cDelta As Double = 4e-6

      For i = 0 To cUB
         aTemp(i) = dW
         dW = dW + cDelta
      Next i

      For i = 1 To cUB
         Debug.Print "Item-" & ( i-1); " is near to Item-" & i; " : ";
NearlyEqual(aTemp(i-1), aTemp(i))
      Next i

      ' But of course:
       Debug.Print "Item-" & 0; " is near to Item-" & cUB; " : ";
NearlyEqual(aTemp(0), aTemp(cUB))
   End Sub

For this reason, I would prefer something like the following instead:

   Function NearlyEqual2(ByVal A As Double, ByVal B As Double) As Boolean
      NearlyEqual2 = (Round(A,5) = Round(B,5))
   End Function

--
PZ
Author
6 Jul 2005 1:41 PM
Rick Rothstein
> In most cases using the 'epsilon' tolerance won't cause any problem.
> Theoretically, however, it isn't quite correct - you break the
transitivity
> of the equality relation. Example (untested):

Unless I'm missing some subtle aspect of your comment, I don't see this
as being significant in any way when we are talking about a "nearly
equal" type of function.


> For this reason, I would prefer something like the following instead:
>
>    Function NearlyEqual2(ByVal A As Double, ByVal B As Double) As
Boolean
>       NearlyEqual2 = (Round(A,5) = Round(B,5))
>    End Function

Yes, you could you this approach also. Actually, I like this approach
better than the epsilon test even if it will probably be ever so
slightly slower... two function calls instead of one(?). However, I
wouldn't use the Round function as can produce transitivity problems of
its own due to its use of Banker's Rounding. Consider this....

Print Round(11.111115, 5), Round(11.111125, 5), Round(11.111135, 5)

Use the Format function instead as that will produce the "correct" type
of rounding.

Function NearlyEqual3(ByVal A As Double, ByVal B As Double) As Boolean
  NearlyEqual3 = (Format(A, "#.#####") = Format(B, "#.#####"))
End Function

Rick
Author
6 Jul 2005 2:05 PM
Bob Butler
"Rick Rothstein" <rickNOSPAMnews@NOSPAMcomcast.net> wrote in message
news:%23JiSoBjgFHA.1948@TK2MSFTNGP12.phx.gbl
> Yes, you could you this approach also. Actually, I like this approach
> better than the epsilon test even if it will probably be ever so
> slightly slower... two function calls instead of one(?). However, I
> wouldn't use the Round function as can produce transitivity problems
> of its own due to its use of Banker's Rounding. Consider this....
>
> Print Round(11.111115, 5), Round(11.111125, 5), Round(11.111135, 5)
>
> Use the Format function instead as that will produce the "correct"
> type of rounding.

If speed is of concern then converting to and from String like that is not
the best idea, especially when wrapping them up in Variants

--
Reply to the group so all can participate
VB.Net: "Fool me once..."
Author
6 Jul 2005 4:50 PM
Pásztor, Zoltán
Inline ...

Rick Rothstein wrote:
>> In most cases using the 'epsilon' tolerance won't cause any problem.
>> Theoretically, however, it isn't quite correct - you break the
>> transitivity of the equality relation. Example (untested):
>
> Unless I'm missing some subtle aspect of your comment, I don't see
> this as being significant in any way when we are talking about a
> "nearly equal" type of function.


When used in simple situations, I totally agree. But if you build a complex
system dealing with floating point values and using this relation
systematically as equivalence criterion, then the effect can be
significant - you can arrive at the result that 0 is equivalent with 1, or
whatever. It seems a rather contrieved example, but I had really encountered
something similar.


Show quoteHide quote
>
>
>> For this reason, I would prefer something like the following instead:
>>
>>    Function NearlyEqual2(ByVal A As Double, ByVal B As Double) As
>>       Boolean NearlyEqual2 = (Round(A,5) = Round(B,5))
>>    End Function
>
> Yes, you could you this approach also. Actually, I like this approach
> better than the epsilon test even if it will probably be ever so
> slightly slower... two function calls instead of one(?). However, I
> wouldn't use the Round function as can produce transitivity problems
> of its own due to its use of Banker's Rounding. Consider this....
>
> Print Round(11.111115, 5), Round(11.111125, 5), Round(11.111135, 5)

Yes, I know about this, but I don't think it leads to transitivity error.
The essential goal is to map the fine-grain machine representation values to
some coarser subset. The rounding method will have effect on the choice of
the 'nearest' allowed representative in borderline cases, but the effect is
not rippling.

As for the efficiency - yes, this is certainly much slower, esp. because
rounding is performed for the decimal place, not very fast on binary
machines. If one's going to need it for heavy duty, it is better to mask off
the tail part of the internal representation of the mantissa.


Show quoteHide quote
>
> Use the Format function instead as that will produce the "correct"
> type of rounding.
>
> Function NearlyEqual3(ByVal A As Double, ByVal B As Double) As Boolean
>   NearlyEqual3 = (Format(A, "#.#####") = Format(B, "#.#####"))
> End Function
>
> Rick
Author
14 Jul 2005 2:34 PM
Chris Eden
Thanks all, for your help

Cheers

Chris



*** Sent via Developersdex http://www.developersdex.com ***