Méthode System.Single.Equals

Cet article vous offre des remarques complémentaires à la documentation de référence pour cette API.

La Single.Equals(Single) méthode implémente l’interface System.IEquatable<T> et s’exécute légèrement mieux que Single.Equals(Object) parce qu’elle n’a pas besoin de convertir le obj paramètre en objet.

Conversions étendues

Selon votre langage de programmation, il peut être possible de coder une Equals méthode où le type de paramètre a moins de bits (est plus étroit) que le type d’instance. Cela est possible, car certains langages de programmation effectuent une conversion étendue implicite qui représente le paramètre en tant que type avec autant de bits que l’instance.

Par exemple, supposons que le type d’instance soit Single et que le type de paramètre soit Int32. Le compilateur Microsoft C# génère des instructions pour représenter la valeur du paramètre en tant qu’objet Single , puis génère une Single.Equals(Single) méthode qui compare les valeurs de l’instance et la représentation étendue du paramètre.

Consultez la documentation de votre langage de programmation pour déterminer si son compilateur effectue des conversions étendues implicites de types numériques. Pour plus d’informations, consultez Tables de conversion de type.

Précision dans les comparaisons

La Equals méthode doit être utilisée avec précaution, car deux valeurs apparemment équivalentes peuvent être inégales en raison de la précision différente des deux valeurs. L’exemple suivant indique que la Single valeur .3333 et le Single retour en divisant 1 par 3 sont inégaux.

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

Une technique de comparaison qui évite les problèmes associés à la comparaison pour l’égalité implique la définition d’une marge acceptable de différence entre deux valeurs (comme .01 % de l’une des valeurs). Si la valeur absolue de la différence entre les deux valeurs est inférieure ou égale à cette marge, la différence est susceptible d’être un résultat des différences de précision et, par conséquent, les valeurs sont susceptibles d’être égales. L’exemple suivant utilise cette technique pour comparer .33333 et 1/3, qui sont les deux Single valeurs que l’exemple de code précédent a trouvées inégales.

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

Dans ce cas, les valeurs sont égales.

Remarque

Étant donné que Epsilon définit l’expression minimale d’une valeur positive dont la plage est proche de zéro, la marge de différence doit être supérieure à Epsilon. En règle générale, il est beaucoup plus grand que Epsilon. En raison de cela, nous vous recommandons de ne pas utiliser Epsilon lors de la comparaison Double des valeurs pour l’égalité.

Une deuxième technique qui évite les problèmes associés à la comparaison pour l’égalité implique de comparer la différence entre deux nombres à virgule flottante avec une valeur absolue. Si la différence est inférieure ou égale à cette valeur absolue, les nombres sont égaux. S’il est plus grand, les nombres ne sont pas égaux. Une façon de procéder consiste à sélectionner arbitrairement une valeur absolue. Toutefois, cela pose problème, car une marge acceptable de différence dépend de l’ampleur des Single valeurs. Une deuxième façon tire parti d’une fonctionnalité de conception du format à virgule flottante : la différence entre les composants mantissa dans les représentations entières de deux valeurs à virgule flottante indique le nombre de valeurs à virgule flottante possibles qui séparent les deux valeurs. Par exemple, la différence entre 0,0 et 1 est égale à Epsilon 1, car Epsilon est la plus petite valeur représentée lors de l’utilisation d’une Single valeur dont la valeur est égale à zéro. L’exemple suivant utilise cette technique pour comparer .333333 et 1/3, qui sont les deux Double valeurs que l’exemple de code précédent avec la Equals(Single) méthode trouvée comme inégale. Notez que l’exemple utilise les méthodes et BitConverter.ToInt32 les BitConverter.GetBytes méthodes pour convertir une valeur à virgule flottante simple précision en sa représentation entière.

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 précision des nombres à virgule flottante au-delà de la précision documentée est spécifique à l’implémentation et à la version de .NET. Par conséquent, une comparaison de deux nombres peut produire des résultats différents en fonction de la version de .NET, car la précision de la représentation interne des nombres peut changer.