System.Single.Equals-Methode

Dieser Artikel enthält ergänzende Hinweise zur Referenzdokumentation für diese API.

Die Single.Equals(Single) Methode implementiert die System.IEquatable<T> Schnittstelle und führt etwas besser aus, als Single.Equals(Object) weil der Parameter nicht in ein Objekt konvertiert obj werden muss.

Widening Conversions (Erweiterungskonvertierungen)

Je nach Programmiersprache kann es möglich sein, eine Equals Methode zu codieren, bei der der Parametertyp weniger Bits aufweist (ist schmaler) als der Instanztyp. Dies ist möglich, da einige Programmiersprachen eine implizite Erweiterungskonvertierung ausführen, die den Parameter als Typ mit so vielen Bits wie die Instanz darstellt.

Angenommen, der Instanztyp ist Single und der Parametertyp ist Int32. Der Microsoft C#-Compiler generiert Anweisungen, um den Wert des Parameters als Single Objekt darzustellen, und generiert dann eine Single.Equals(Single) Methode, die die Werte der Instanz und die erweiterte Darstellung des Parameters vergleicht.

In der Dokumentation der Programmiersprache können Sie ermitteln, ob der Compiler implizite Erweiterungskonvertierungen numerischer Typen durchführt. Weitere Informationen finden Sie unter Typkonvertierungstabellen.

Genauigkeit in Vergleichen

Die Equals Methode sollte mit Vorsicht verwendet werden, da zwei scheinbar gleichwertige Werte aufgrund der unterschiedlichen Genauigkeit der beiden Werte ungleich sein können. Im folgenden Beispiel wird berichtet, dass der Single Wert 0,3333 und der Single durch Dividieren von 1 durch 3 zurückgegebene Wert ungleich sind.

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

Eine Vergleichsmethode, der vermeidet Probleme im Zusammenhang mit Vergleich für Gleichheit umfasst eine akzeptable Rand Unterschied zwischen zwei Werten definieren (z. B. 01 % einer der Werte). Wenn der absolute Wert der Differenz zwischen den beiden Werten kleiner oder gleich diesem Rand ist, ist die Differenz wahrscheinlich ein Ergebnis von Unterschieden in der Genauigkeit und daher sind die Werte wahrscheinlich gleich. Im folgenden Beispiel wird diese Technik verwendet, um .3333 und 1/3 zu vergleichen. Dabei handelt es sich um die beiden Single Werte, die im vorherigen Codebeispiel ungleich sind.

// 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 diesem Fall sind die Werte gleich.

Hinweis

Da Epsilon der Minimalausdruck eines positiven Werts definiert wird, dessen Bereich nahe Null liegt, muss der Differenzrand größer als Epsilonsein. In der Regel ist sie oft größer als Epsilon. Aus diesem Grund wird empfohlen, beim Vergleichen Double von Werten für gleichheitsgleiche Werte nicht zu verwendenEpsilon.

Eine zweite Technik, mit der die Probleme vermieden werden, die mit dem Vergleichen von Gleichheit verbunden sind, besteht darin, die Differenz zwischen zwei Gleitkommazahlen mit einem bestimmten absoluten Wert zu vergleichen. Wenn die Differenz kleiner oder gleich diesem absoluten Wert ist, sind die Zahlen gleich. Wenn sie größer ist, sind die Zahlen nicht gleich. Eine Möglichkeit hierfür ist die willkürliche Auswahl eines absoluten Werts. Dies ist jedoch problematisch, da ein akzeptabler Differenzrand von der Größe der Single Werte abhängt. Eine zweite Möglichkeit nutzt ein Entwurfsfeature des Gleitkommaformats: Der Unterschied zwischen den Mantissa-Komponenten in den ganzzahligen Darstellungen von zwei Gleitkommawerten gibt die Anzahl möglicher Gleitkommawerte an, die die beiden Werte trennen. Der Unterschied zwischen 0,0 und Epsilon 1 Epsilon ist beispielsweise der kleinste darstellbare Wert beim Arbeiten mit einem Single Wert, dessen Wert null ist. Im folgenden Beispiel wird diese Technik verwendet, um .33333 und 1/3 zu vergleichen. Dabei handelt es sich um die beiden Double Werte, die im vorherigen Codebeispiel mit der Equals(Single) Methode ungleich sind. Beachten Sie, dass im Beispiel die BitConverter.GetBytes Und-Methoden BitConverter.ToInt32 verwendet werden, um einen Gleitkommawert mit einfacher Genauigkeit in seine ganze Zahl zu konvertieren.

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

Die Genauigkeit von Gleitkommazahlen über die dokumentierte Genauigkeit hinaus ist spezifisch für die Implementierung und Version von .NET. Folglich kann ein Vergleich zweier Zahlen je nach Version von .NET unterschiedliche Ergebnisse erzeugen, da sich die Genauigkeit der internen Darstellung der Zahlen ändern kann.