Condividi tramite


Metodo System.Double.Equals

Il metodo Th Double.Equals(Double) implementa l'interfaccia System.IEquatable<T> e offre prestazioni leggermente migliori rispetto Double.Equals(Object) al fatto che non è necessario convertire il obj parametro in un oggetto .

Conversioni che supportano un maggior numero di dati

A seconda del linguaggio di programmazione, potrebbe essere possibile codificare un Equals metodo in cui il tipo di parametro ha meno bit (è più piccolo) rispetto al tipo di istanza. Ciò è possibile perché alcuni linguaggi di programmazione eseguono una conversione implicita di estensione che rappresenta il parametro come tipo con il maggior numero di bit dell'istanza.

Si supponga, ad esempio, che il tipo di istanza sia Double e che il tipo di parametro sia Int32. Il compilatore Microsoft C# genera istruzioni per rappresentare il valore del parametro come Double oggetto, quindi genera un Double.Equals(Double) metodo che confronta i valori dell'istanza e la rappresentazione estesa del parametro.

Consultare la documentazione del linguaggio di programmazione per determinare se il compilatore esegue conversioni implicite di tipi numerici verso l'ampliamento. Per altre informazioni, vedere l'argomento Tabelle di conversione dei tipi.

Precisione nei confronti

Il Equals metodo deve essere usato con cautela, perché due valori apparentemente equivalenti possono essere diversi a causa della precisione diversa dei due valori. Nell'esempio seguente viene segnalato che il Double valore .333333 e il Double valore restituito dividendo 1 per 3 non sono uguali.

// 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

Invece di confrontare l'uguaglianza, una tecnica prevede la definizione di un margine relativo accettabile di differenza tra due valori (ad esempio,001% di uno dei valori). Se il valore assoluto della differenza tra i due valori è minore o uguale a tale margine, è probabile che la differenza sia dovuta a differenze di precisione e, di conseguenza, i valori saranno uguali. Nell'esempio seguente viene usata questa tecnica per confrontare .33333 e 1/3, i due Double valori rilevati dall'esempio di codice precedente non sono uguali. In questo caso, i valori sono uguali.

// 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

Poiché Epsilon definisce l'espressione minima di un valore positivo il cui intervallo è vicino a zero, il margine di differenza tra due valori simili deve essere maggiore di Epsilon. In genere, è molte volte maggiore di Epsilon. Per questo motivo, è consigliabile non usare Epsilon durante il confronto dei Double valori per l'uguaglianza.

Una seconda tecnica prevede il confronto tra due numeri a virgola mobile con un valore assoluto. Se la differenza è minore o uguale a tale valore assoluto, i numeri sono uguali. Se è maggiore, i numeri non sono uguali. Un'alternativa consiste nel selezionare arbitrariamente un valore assoluto. Questo è problematico, tuttavia, perché un margine di differenza accettabile dipende dalla grandezza dei Double valori. Una seconda alternativa sfrutta una funzionalità di progettazione del formato a virgola mobile: la differenza tra la rappresentazione integer di due valori a virgola mobile indica il numero di possibili valori a virgola mobile che li separano. Ad esempio, la differenza tra 0,0 e Epsilon 1, perché Epsilon è il valore rappresentabile più piccolo quando si lavora con un il Double cui valore è zero. Nell'esempio seguente viene usata questa tecnica per confrontare .33333 e 1/3, che sono i due Double valori che l'esempio di codice precedente con il Equals(Double) metodo risulta diverso. Nell'esempio viene utilizzato il BitConverter.DoubleToInt64Bits metodo per convertire un valore a virgola mobile e precisione doppia nella relativa rappresentazione integer. Nell'esempio vengono dichiarati i valori come uguali se non sono presenti valori a virgola mobile possibili tra le relative rappresentazioni integer.

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

Per alcuni valori, è possibile considerarli uguali anche quando è presente un valore a virgola mobile possibile tra le rappresentazioni integer. Si considerino, ad esempio, i valori 0.39 double e 1.69 - 1.3 (che viene calcolato come 0.3899999999999999). In un computer little-endian, le rappresentazioni integer di questi valori sono 4600697235336603894 rispettivamente e 4600697235336603892. La differenza tra i valori integer è 2, ovvero esiste un possibile valore a virgola mobile tra 0.39 e 1.69 - 1.3.

Differenze di versione

La precisione dei numeri a virgola mobile oltre la precisione documentata è specifica per l'implementazione e la versione di .NET. Di conseguenza, un confronto di due numeri particolari potrebbe cambiare tra le versioni di .NET perché la precisione della rappresentazione interna dei numeri potrebbe cambiare.

NaN

Se due Double.NaN valori vengono testati per verificarne l'uguaglianza chiamando il Equals metodo , il metodo restituisce true. Tuttavia, se due Double.NaN valori vengono testati per verificare l'uguaglianza usando l'operatore di uguaglianza, l'operatore restituisce false. Quando si desidera determinare se il valore di un Double oggetto non è un numero (NaN), un'alternativa consiste nel chiamare il IsNaN metodo .