Comparar doble en problema de precisión VBA
-
04-07-2019 - |
Pregunta
Tengo problemas para comparar 2 dobles en Excel VBA
supongamos que tengo el siguiente código
Dim a as double
Dim b as double
a = 0.15
b = 0.01
Después de algunas manipulaciones en b, b ahora es igual a 0.6
sin embargo, la imprecisión relacionada con el doble tipo de datos me da dolor de cabeza porque
if a = b then
//this will never trigger
end if
¿Sabe cómo puedo eliminar la imprecisión final en el tipo doble?
Solución
No se pueden comparar valores de punto flotante por igualdad. Consulte este artículo en " Comparación de punto flotante números " para una discusión de cómo manejar el error intrínseco.
No es tan simple como compararlo con un margen de error constante a menos que sepa con seguridad cuál es el rango absoluto de los flotadores de antemano.
Otros consejos
si vas a hacer esto ....
Dim a as double
Dim b as double
a = 0.15
b = 0.01
debe agregar la función de redondeo en su declaración IF de esta forma ...
If Round(a,2) = Round(b,2) Then
//code inside block will now trigger.
End If
Consulte también aquí para obtener referencias adicionales de Microsoft .
Nunca es aconsejable comparar dobles en igualdad.
Algunos valores decimales se asignan a varias representaciones de punto flotante. Entonces, un 0.6 no siempre es igual al otro 0.6.
Si restamos uno del otro, probablemente obtengamos algo como 0.00000000051.
Ahora podemos definir la igualdad como una diferencia menor que un cierto margen de error.
Aquí hay una función simple que escribí:
Function dblCheckTheSame(number1 As Double, number2 As Double, Optional Digits As Integer = 12) As Boolean
If (number1 - number2) ^ 2 < (10 ^ -Digits) ^ 2 Then
dblCheckTheSame = True
Else
dblCheckTheSame = False
End If
End Function
Llámalo con:
MsgBox dblCheckTheSame(1.2345, 1.23456789)
MsgBox dblCheckTheSame(1.2345, 1.23456789, 4)
MsgBox dblCheckTheSame(1.2345678900001, 1.2345678900002)
MsgBox dblCheckTheSame(1.2345678900001, 1.2345678900002, 14)
Como se ha señalado, muchos números decimales no se pueden representar precisamente como tipos tradicionales de punto flotante. Dependiendo de la naturaleza de su espacio problemático, puede ser mejor usar el tipo Decimal VBA que puede representar números decimales (base 10) con una precisión perfecta hasta cierto punto decimal. Esto se hace a menudo para representar el dinero, por ejemplo, donde a menudo se desea una precisión decimal de 2 dígitos.
Dim a as Decimal
Dim b as Decimal
a = 0.15
b = 0.01
El tipo de datos de Moneda puede ser una buena alternativa. Maneja números relativamente grandes con una precisión fija de cuatro dígitos.
¿Trabajar una ronda? No estoy seguro de si esto responderá a todos los escenarios, pero me encontré con un problema al comparar valores dobles redondeados en VBA. Cuando comparé con números que parecían ser idénticos después del redondeo, VBA activaría falso en una declaración de comparación si-entonces. Mi solución fue ejecutar dos conversiones, primero doble a cadena, luego cadena a doble y luego hacer la comparación.
Ejemplo simulado No registré los números exactos que causaron el error mencionado en esta publicación, y las cantidades en mi ejemplo no desencadenan el problema actualmente y están destinados a representar el tipo de problema.
Sub Test_Rounded_Numbers()
Dim Num1 As Double
Dim Num2 As Double
Let Num1 = 123.123456789
Let Num2 = 123.123467891
Let Num1 = Round(Num1, 4) '123.1235
Let Num2 = Round(Num2, 4) '123.1235
If Num1 = Num2 Then
MsgBox "Correct Match, " & Num1 & " does equal " & Num2
Else
MsgBox "Inccorrect Match, " & Num1 & " does not equal " & Num2
End If
'Here it would say that "Inccorrect Match, 123.1235 does not equal 123.1235."
End Sub
Sub Fixed_Double_Value_Type_Compare_Issue()
Dim Num1 As Double
Dim Num2 As Double
Let Num1 = 123.123456789
Let Num2 = 123.123467891
Let Num1 = Round(Num1, 4) '123.1235
Let Num2 = Round(Num2, 4) '123.1235
'Add CDbl(CStr(Double_Value))
'By doing this step the numbers
'would trigger if they matched
'100% of the time
If CDbl(CStr(Num1)) = CDbl(CStr(Num2)) Then
MsgBox "Correct Match"
Else
MsgBox "Inccorrect Match"
End If
'Now it says Here it would say that "Correct Match, 123.1235 does equal 123.1235."
End Sub