Condividi tramite


Metodo System.Single.Equals

Questo articolo fornisce osservazioni supplementari alla documentazione di riferimento per questa API.

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

Conversioni di estensione

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 Single e che il tipo di parametro sia Int32. Il compilatore Microsoft C# genera istruzioni per rappresentare il valore del parametro come Single oggetto e quindi genera un Single.Equals(Single) 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 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 Single valore .3333 e l'oggetto Single restituito dividendo 1 per 3 non sono uguali.

// Initialize two floats with apparently identical values
float float1 = .33333f;
float float2 = 1/3;
// Compare them for equality
Console.WriteLine(float1.Equals(float2));    // displays false
// Initialize two floats with apparently identical values
let float1 = 0.33333f
let float2 = 1f / 3f
// Compare them for equality
printfn $"{float1.Equals float2}"    // displays false
' Initialize two singles with apparently identical values
Dim single1 As Single = .33333
Dim single2 As Single = 1/3
' Compare them for equality
Console.WriteLine(single1.Equals(single2))    ' displays False

Una tecnica di confronto che evita i problemi associati al confronto per l'uguaglianza comporta la definizione di un margine di differenza accettabile tra due valori (ad esempio,01% 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 un risultato delle differenze di precisione e, pertanto, i valori saranno uguali. Nell'esempio seguente viene usata questa tecnica per confrontare .33333 e 1/3, ovvero i due Single valori rilevati dall'esempio di codice precedente non uguali.

// Initialize two floats with apparently identical values
float float1 = .33333f;
float float2 = (float) 1/3;
// Define the tolerance for variation in their values
float difference = Math.Abs(float1 * .0001f);

// Compare the values
// The output to the console indicates that the two values are equal
if (Math.Abs(float1 - float2) <= difference)
   Console.WriteLine("float1 and float2 are equal.");
else
   Console.WriteLine("float1 and float2 are unequal.");
// Initialize two floats with apparently identical values
let float1 = 0.33333f
let float2 = 1f / 3f
// Define the tolerance for variation in their values
let difference = abs (float1 * 0.0001f)

// Compare the values
// The output to the console indicates that the two values are equal
if abs (float1 - float2) <= difference then
    printfn "float1 and float2 are equal."
else
    printfn "float1 and float2 are unequal."
' Initialize two singles with apparently identical values
Dim single1 As Single = .33333
Dim single2 As Single = 1/3
' Define the tolerance for variation in their values
Dim difference As Single = Math.Abs(single1 * .0001f)

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

In questo caso, i valori sono uguali.

Nota

Poiché Epsilon definisce l'espressione minima di un valore positivo il cui intervallo è vicino a zero, il margine di differenza 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 che evita i problemi associati al confronto dell'uguaglianza comporta 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 modo per eseguire questa operazione consiste nel selezionare arbitrariamente un valore assoluto. Tuttavia, questo è problematico, perché un margine di differenza accettabile dipende dalla grandezza dei Single valori. Un secondo modo sfrutta una funzionalità di progettazione del formato a virgola mobile: la differenza tra i componenti mantissa nelle rappresentazioni integer di due valori a virgola mobile indica il numero di possibili valori a virgola mobile che separa i due valori. Ad esempio, la differenza tra 0,0 e Epsilon 1, perché Epsilon è il valore rappresentabile più piccolo quando si lavora con un il Single 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(Single) metodo risulta diverso. Si noti che nell'esempio vengono utilizzati i BitConverter.GetBytes metodi e BitConverter.ToInt32 per convertire un valore a virgola mobile a precisione singola nella relativa rappresentazione integer.

using System;

public class Example
{
   public static void Main()
   {
      float value1 = .1f * 10f;
      float value2 = 0f;
      for (int ctr = 0; ctr < 10; ctr++)
         value2 += .1f;
         
      Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,
                        HasMinimalDifference(value1, value2, 1));
   }

   public static bool HasMinimalDifference(float value1, float value2, int units)
   {
      byte[] bytes = BitConverter.GetBytes(value1);
      int iValue1 = BitConverter.ToInt32(bytes, 0);
      
      bytes = BitConverter.GetBytes(value2);
      int iValue2 = BitConverter.ToInt32(bytes, 0);
      
      // If the signs are different, return false except for +0 and -0.
      if ((iValue1 >> 31) != (iValue2 >> 31))
      {
         if (value1 == value2)
            return true;
          
         return false;
      }

      int diff = Math.Abs(iValue1 - iValue2);

      if (diff <= units)
         return true;

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

let hasMinimalDifference (value1: float32) (value2: float32) units =
    let bytes = BitConverter.GetBytes value1
    let iValue1 = BitConverter.ToInt32(bytes, 0)
    let bytes = BitConverter.GetBytes(value2)
    let iValue2 = BitConverter.ToInt32(bytes, 0)
    
    // If the signs are different, return false except for +0 and -0.
    if (iValue1 >>> 31) <> (iValue2 >>> 31) then
        value1 = value2
    else
        let diff = abs (iValue1 - iValue2)
        diff <= units

let value1 = 0.1f * 10f
let value2 =
    List.replicate 10 0.1f
    |> List.sum
    
printfn $"{value1:R} = {value2:R}: {hasMinimalDifference value1 value2 1}"
// The example displays the following output:
//        1 = 1.0000001: True
Module Example
   Public Sub Main()
      Dim value1 As Single = .1 * 10
      Dim value2 As Single = 0
      For ctr As Integer =  0 To 9
         value2 += CSng(.1)
      Next
               
      Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,
                        HasMinimalDifference(value1, value2, 1))
   End Sub

   Public Function HasMinimalDifference(value1 As Single, value2 As Single, units As Integer) As Boolean
      Dim bytes() As Byte = BitConverter.GetBytes(value1)
      Dim iValue1 As Integer =  BitConverter.ToInt32(bytes, 0)
      
      bytes = BitConverter.GetBytes(value2)
      Dim iValue2 As Integer =  BitConverter.ToInt32(bytes, 0)
      
      ' If the signs are different, Return False except for +0 and -0.
      If ((iValue1 >> 31) <> (iValue2 >> 31)) Then
         If value1 = value2 Then
            Return True
         End If           
         Return False
      End If

      Dim diff As Integer =  Math.Abs(iValue1 - iValue2)

      If diff <= units Then
         Return True
      End If

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

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 potrebbe produrre risultati diversi a seconda della versione di .NET, perché la precisione della rappresentazione interna dei numeri potrebbe cambiare.