Почему это сравнение базовых чисел дает нелогичный результат? - PullRequest
0 голосов
/ 08 февраля 2019

Охотясь на странную ошибку в моем приложении 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 голосов
/ 08 февраля 2019

Как насчет целочисленной арифметики?

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
0 голосов
/ 08 февраля 2019

Если вы не можете использовать Decimal для своих расчетов, то вы должны написать свой код, чтобы учесть тот факт, что двоичные типы с плавающей запятой не могут точно представлять некоторые дробные значения.Поэтому вместо того, чтобы проверять, равны ли числа, вы проверяете, равны ли они почти .

. Вы можете использовать код, подобный следующему (взято из этой статьи Михаэля Боргвардта :

Это перевод 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
0 голосов
/ 08 февраля 2019

Вы получаете эти проблемы, потому что вы используете типы с плавающей точкой, которые используют базу 2, а база 2 не может точно представить некоторые дробные значения.

Вот почему типы с фиксированной точкой, такие как 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
...