Compartir a través de


Método System.Double.Equals

El método Th Double.Equals(Double) implementa la System.IEquatable<T> interfaz y funciona ligeramente mejor que Double.Equals(Object) porque no tiene que convertir el obj parámetro en un objeto.

Conversiones de ampliación

En función del lenguaje de programación, puede ser posible codificar un Equals método en el que el tipo de parámetro tenga menos bits (es más estrecho) que el tipo de instancia. Esto es posible porque algunos lenguajes de programación realizan una conversión de ampliación implícita que representa el parámetro como un tipo con tantos bits como la instancia.

Por ejemplo, supongamos que el tipo de instancia es Double y el tipo de parámetro es Int32. El compilador de Microsoft C# genera instrucciones para representar el valor del parámetro como un Double objeto y, a continuación, genera un Double.Equals(Double) método que compara los valores de la instancia y la representación ampliada del parámetro.

Consulte la documentación del lenguaje de programación para determinar si su compilador realiza conversiones implícitas de ampliación de tipos numéricos. Para obtener más información, vea el tema Tablas de conversión de tipos.

Precisión en comparaciones

El Equals método debe usarse con precaución, ya que dos valores aparentemente equivalentes pueden ser diferentes debido a la precisión diferente de los dos valores. En el ejemplo siguiente se informa de que el Double valor .333333 y el Double valor devuelto dividiendo 1 por 3 no son iguales.

// Initialize two doubles with apparently identical values
double double1 = .33333;
double double2 = (double) 1/3;
// Compare them for equality
Console.WriteLine(double1.Equals(double2));    // displays false
// Initialize two doubles with apparently identical values
let double1 = 0.33333
let double2 = double (1 / 3)
// Compare them for equality
printfn $"{double1.Equals double2}"    // displays false
' Initialize two doubles with apparently identical values
Dim double1 As Double = .33333
Dim double2 As Double = 1/3
' Compare them for equality
Console.WriteLine(double1.Equals(double2))    ' displays False

En lugar de comparar la igualdad, una técnica implica definir un margen relativo aceptable de diferencia entre dos valores (como .001 % de uno de los valores). Si el valor absoluto de la diferencia entre los dos valores es menor o igual que ese margen, es probable que la diferencia se deba a diferencias de precisión y, por lo tanto, es probable que los valores sean iguales. En el ejemplo siguiente se usa esta técnica para comparar .33333 y 1/3, los dos Double valores que el ejemplo de código anterior encontró que no son iguales. En este caso, los valores son iguales.

// Initialize two doubles with apparently identical values
double double1 = .333333;
double double2 = (double) 1/3;
// Define the tolerance for variation in their values
double difference = Math.Abs(double1 * .00001);

// Compare the values
// The output to the console indicates that the two values are equal
if (Math.Abs(double1 - double2) <= difference)
   Console.WriteLine("double1 and double2 are equal.");
else
   Console.WriteLine("double1 and double2 are unequal.");
// Initialize two doubles with apparently identical values
let double1 = 0.333333
let double2 = double (1 / 3)
// Define the tolerance for variation in their values
let difference = abs (double1 * 0.00001)

// Compare the values
// The output to the console indicates that the two values are equal
if abs (double1 - double2) <= difference then
    printfn "double1 and double2 are equal."
else
    printfn "double1 and double2 are unequal."
' Initialize two doubles with apparently identical values
Dim double1 As Double = .33333
Dim double2 As Double = 1/3
' Define the tolerance for variation in their values
Dim difference As Double = Math.Abs(double1 * .00001)

' Compare the values
' The output to the console indicates that the two values are equal
If Math.Abs(double1 - double2) <= difference Then
   Console.WriteLine("double1 and double2 are equal.")
Else
   Console.WriteLine("double1 and double2 are unequal.")
End If

Nota:

Dado Epsilon que define la expresión mínima de un valor positivo cuyo intervalo está cerca de cero, el margen de diferencia entre dos valores similares debe ser mayor que Epsilon. Normalmente, es muchas veces mayor que Epsilon. Por este motivo, se recomienda no usar Epsilon al comparar Double los valores de igualdad.

Una segunda técnica implica comparar la diferencia entre dos números de punto flotante con algún valor absoluto. Si la diferencia es menor o igual que ese valor absoluto, los números son iguales. Si es mayor, los números no son iguales. Una alternativa es seleccionar arbitrariamente un valor absoluto. Sin embargo, esto es problemático, ya que un margen aceptable de diferencia depende de la magnitud de los Double valores. Una segunda alternativa aprovecha una característica de diseño del formato de punto flotante: la diferencia entre la representación entera de dos valores de punto flotante indica el número de posibles valores de punto flotante que los separa. Por ejemplo, la diferencia entre 0,0 y Epsilon es 1, porque Epsilon es el valor representable más pequeño al trabajar con un Double cuyo valor es cero. En el ejemplo siguiente se usa esta técnica para comparar .33333 y 1/3, que son los dos Double valores que el ejemplo de código anterior con el Equals(Double) método encontró que no es igual. En el ejemplo se usa el BitConverter.DoubleToInt64Bits método para convertir un valor de punto flotante de precisión doble en su representación entera. En el ejemplo se declaran los valores iguales si no hay valores de punto flotante posibles entre sus representaciones de enteros.

public static void Main()
{
    // Initialize the values.
    double value1 = .1 * 10;
    double value2 = 0;
    for (int ctr = 0; ctr < 10; ctr++)
        value2 += .1;

    Console.WriteLine($"{value1:R} = {value2:R}: " +
        $"{HasMinimalDifference(value1, value2, 1)}");
}

public static bool HasMinimalDifference(
    double value1,
    double value2,
    int allowableDifference
    )
{
    // Convert the double values to long values.
    long lValue1 = BitConverter.DoubleToInt64Bits(value1);
    long lValue2 = BitConverter.DoubleToInt64Bits(value2);

    // If the signs are different, return false except for +0 and -0.
    if ((lValue1 >> 63) != (lValue2 >> 63))
    {
        if (value1 == value2)
            return true;

        return false;
    }

    // Calculate the number of possible
    // floating-point values in the difference.
    long diff = Math.Abs(lValue1 - lValue2);

    if (diff <= allowableDifference)
        return true;

    return false;
}
// The example displays the following output:
//
//        1 = 0.99999999999999989: True
open System

let hasMinimalDifference (value1: double) (value2: double) (units: int) =
    let lValue1 = BitConverter.DoubleToInt64Bits value1
    let lValue2 = BitConverter.DoubleToInt64Bits value2

    // If the signs are different, return false except for +0 and -0.
    if (lValue1 >>> 63) <> (lValue2 >>> 63) then
        value1 = value2
    else
        let diff = abs (lValue1 - lValue2)

        diff <= int64 units

let value1 = 0.1 * 10.
let mutable value2 = 0.
for _ = 0 to 9 do
    value2 <- value2 + 0.1

printfn $"{value1:R} = {value2:R}: {hasMinimalDifference value1 value2 1}"
                

// The example displays the following output:
//        1 = 0.99999999999999989: True
Module Example
   Public Sub Main()
      Dim value1 As Double = .1 * 10
      Dim value2 As Double = 0
      For ctr As Integer =  0 To 9
         value2 += .1
      Next
               
      Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,
                        HasMinimalDifference(value1, value2, 1))
   End Sub

   Public Function HasMinimalDifference(value1 As Double, value2 As Double, units As Integer) As Boolean
      Dim lValue1 As long =  BitConverter.DoubleToInt64Bits(value1)
      Dim lValue2 As long =  BitConverter.DoubleToInt64Bits(value2)
      
      ' If the signs are different, Return False except for +0 and -0.
      If ((lValue1 >> 63) <> (lValue2 >> 63)) Then
         If value1 = value2 Then
            Return True
         End If           
         Return False
      End If

      Dim diff As Long =  Math.Abs(lValue1 - lValue2)

      If diff <= units Then
         Return True
      End If

      Return False
   End Function
End Module
' The example displays the following output:
'       1 = 0.99999999999999989: True

Nota:

Para algunos valores, puede considerarlos iguales incluso cuando hay un valor de punto flotante posible entre las representaciones de enteros. Por ejemplo, considere los valores 0.39 dobles y 1.69 - 1.3 (que se calcula como 0.3899999999999999). En un equipo little-endian, las representaciones de enteros de estos valores son 4600697235336603894 y 4600697235336603892, respectivamente. La diferencia entre los valores enteros es 2, lo que significa que hay un valor de punto flotante posible entre 0.39 y 1.69 - 1.3.

Diferencias de versión

La precisión de los números de punto flotante más allá de la precisión documentada es específica de la implementación y la versión de .NET. Por lo tanto, una comparación de dos números concretos podría cambiar entre versiones de .NET porque la precisión de la representación interna de los números podría cambiar.

NaN

Si se prueban dos Double.NaN valores para obtener igualdad llamando al Equals método , el método devuelve true. Sin embargo, si se prueban dos Double.NaN valores para obtener igualdad mediante el operador de igualdad, el operador devuelve false. Si desea determinar si el valor de un Double no es un número (NaN), una alternativa es llamar al IsNaN método .