为什么这个基本数字比较会产生不合逻辑的结果?

时间:2019-02-07 23:26:51

标签: vb.net comparison logical-operators

在我的VB.NET应用程序中发现一个奇怪的错误时,我将其跟踪到一个令人费解的令人困惑的细节。这是一个简单的测试代码:

If 0.01 > 0.12 - 0.11 Then Debug.Print("what the hell")

0.12-0.11是0.01 ...等于比较的左侧。但是,当我运行此命令时,调试会打印“到底是什么” ...因为很严重,到底是什么。这些数字相等。

另外,如果我有这样的循环:

Dim count As Integer = 0
For i As Double = 0.11 to 0.12 Step 0.01
   count += 1
Next
Debug.Print(count)

它打印1,表示该循环只执行一次,而它应该执行两次。

令人惊讶的是,如果我将上述示例中的0.11、0.12和0.01分别更改为0.1、0.2和0.1,则第一个示例将不打印任何内容,第二个示例将按要求打印2。

这是怎么回事?我是否错过了非常明显的东西,或者这是某种浮点错误或其他东西?

3 个答案:

答案 0 :(得分:1)

之所以遇到这些问题,是因为您使用的浮点类型使用的是基数2,而基数2不能精确表示某些分数值。

这就是为什么设计诸如Decimal之类的定点类型的原因。如果您的示例代码针对定点进行了重新处理(使用Decimal),则可以得到预期的结果。

    ' Specify Decimal constants and this will worked as anticipated.
    If 0.01D > 0.12D - 0.11D Then Debug.Print("what the hell")

    ' 0.12-0.11 Is 0.01... Which Is equal to the left side of the comparison.
    ' However, when I run this, the debug prints "what the hell"... Because 
    ' seriously, what the hell. These numbers are equal.

    ' Additionally, If I have a cycle Like this

    Dim count As Integer = 0
    For i As Decimal = 0.11D To 0.12D Step 0.01D
        count += 1
    Next

    Debug.Print(count) ' Prints 2

答案 1 :(得分:0)

整数算术如何?

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim count As Integer = 0
    For i As Integer = CInt(0.11 * 100) To CInt(0.12 * 100) Step CInt(0.01 * 100)
        count += 1
    Next
    Debug.Print(count.ToString)
End Sub

Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    If CInt(0.01 * 100) > CInt(0.12 * 100) - CInt(0.11 * 100) Then
        Debug.Print("what the hell")
    Else
        Debug.Print("It's Ok")
    End If
End Sub

答案 2 :(得分:0)

如果不能使用Decimal进行计算,则必须编写代码以说明二进制浮点类型不能精确表示某些小数值的事实。因此,您无需检查数字是否相等,而是检查它们是否几乎相等

您可以使用以下代码(取自this article by Michael Borgwardt

这是VB翻译,但未经广泛测试。

Public Shared Function NearlyEqual(a As Double, b As Double, epsilon As Double) As Boolean
    Dim absA As Double = Math.Abs(a)
    Dim absB As Double = Math.Abs(b)
    Dim diff As Double = Math.Abs(a - b)

    If (a = b) Then
        'shortcut, handles infinities
        Return True
    Else 
        If (a = 0 OrElse b = 0 OrElse diff < Double.Epsilon) 
            'a or b is zero or both are extremely close to it
            'relative error is less meaningful here
            Return diff < epsilon
        Else
            'use relative error
            Return diff / (absA + absB) < epsilon
        End If
    End If
End Function