System.Single.Equals-methode

Opmerking

In dit artikel vindt u aanvullende opmerkingen in de referentiedocumentatie voor deze API.

De Single.Equals(Single) methode implementeert de System.IEquatable<T> interface en presteert iets beter dan Single.Equals(Object) omdat deze de obj parameter niet hoeft te converteren naar een object.

Uitbreidende conversies

Afhankelijk van uw programmeertaal is het mogelijk om een Equals methode te codeeren waarbij het parametertype minder bits heeft (kleiner is) dan het exemplaartype. Dit is mogelijk omdat sommige programmeertalen een impliciete widening conversie uitvoeren die de parameter vertegenwoordigt als een type met zoveel bits als het exemplaar.

Stel dat het exemplaartype is Single en het parametertype is Int32. De Microsoft C#-compiler genereert instructies om de waarde van de parameter als een Single object weer te geven en genereert vervolgens een Single.Equals(Single) methode waarmee de waarden van het exemplaar en de verbrede weergave van de parameter worden vergeleken.

Raadpleeg de documentatie van uw programmeertaal om te bepalen of de compiler impliciete widening conversies van numerieke typen uitvoert. Zie Type conversietabellen voor meer informatie.

Precisie in vergelijkingen

De Equals methode moet voorzichtig worden gebruikt, omdat twee schijnbaar gelijkwaardige waarden ongelijk kunnen zijn vanwege de verschillende precisie van de twee waarden. In het volgende voorbeeld wordt gerapporteerd dat de Single waarde .3333 en de Single geretourneerde waarde door 1 met 3 te delen ongelijk zijn.

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

Een vergelijkingstechniek die de problemen voor het vergelijken van gelijkheid vermijdt, omvat het definiëren van een acceptabele marge van verschil tussen twee waarden (zoals .01% van een van de waarden). Als de absolute waarde van het verschil tussen de twee waarden kleiner is dan of gelijk is aan die marge, is het verschil waarschijnlijk een resultaat van verschillen in precisie en zijn de waarden waarschijnlijk gelijk. In het volgende voorbeeld wordt deze techniek gebruikt om .33333 en 1/3 te vergelijken. Dit zijn de twee Single waarden die in het vorige codevoorbeeld ongelijk zijn bevonden.

// 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 dit geval zijn de waarden gelijk.

Opmerking

Omdat Epsilon de minimumexpressie van een positieve waarde waarvan het bereik bijna nul is, definieert, moet de marge van het verschil groter zijn dan Epsilon. Meestal is het veel groter dan Epsilon. Daarom raden we u aan niet te gebruiken Epsilon bij het vergelijken Double van waarden voor gelijkheid.

Een tweede techniek die de problemen met betrekking tot het vergelijken voor gelijkheid vermijdt, omvat het vergelijken van het verschil tussen twee drijvendekommanummers met een absolute waarde. Als het verschil kleiner is dan of gelijk is aan die absolute waarde, zijn de getallen gelijk. Als deze groter is, zijn de getallen niet gelijk. Een manier om dit te doen, is door willekeurig een absolute waarde te selecteren. Dit is echter problematisch, omdat een acceptabele marge van verschil afhankelijk is van de grootte van de Single waarden. Een tweede manier maakt gebruik van een ontwerpeigenschap van de drijvendekomma-indeling: Het verschil tussen de mantissacomponenten in de gehele getalrepresentaties van twee drijvendekommawaarden geeft het aantal mogelijke drijvendekommawaarden aan dat de twee waarden scheidt. Het verschil tussen 0,0 en Epsilon is bijvoorbeeld 1, omdat Epsilon dit de kleinste vertegenwoordigbare waarde is bij het werken met een waarde waarvan de Single waarde nul is. In het volgende voorbeeld wordt deze techniek gebruikt om .33333 en 1/3 te vergelijken. Dit zijn de twee Double waarden die in het vorige codevoorbeeld met de Equals(Single) methode ongelijk zijn gevonden. Houd er rekening mee dat in het voorbeeld de BitConverter.GetBytes en BitConverter.ToInt32 methoden worden gebruikt om een drijvendekommawaarde met één precisie te converteren naar de gehele getalweergave.

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($"{value1:R} = {value2:R}: {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

De nauwkeurigheid van drijvendekommagetallen die verder gaat dan de gedocumenteerde precisie is afhankelijk van de implementatie en versie van .NET. Daarom kan een vergelijking van twee getallen verschillende resultaten opleveren, afhankelijk van de versie van .NET, omdat de precisie van de interne weergave van de getallen kan veranderen.