Sdílet prostřednictvím


Metoda System.Single.Equals

Tento článek obsahuje doplňující poznámky k referenční dokumentaci pro toto rozhraní API.

Metoda Single.Equals(Single) implementuje System.IEquatable<T> rozhraní a provádí mírně lépe, než Single.Equals(Object) protože nemusí převést obj parametr na objekt.

Rozšiřující převody

V závislosti na programovacím jazyce může být možné zakódovat metodu Equals , ve které má typ parametru méně bitů (je užší) než typ instance. Je to možné, protože některé programovací jazyky provádějí implicitní rozšiřující převod, který představuje parametr jako typ s tolika bity, kolik bitů má instance.

Předpokládejme například, že typ instance je Single a typ parametru je Int32. Kompilátor Jazyka Microsoft C# generuje instrukce představující hodnotu parametru jako Single objekt a potom vygeneruje metodu Single.Equals(Single) , která porovnává hodnoty instance a rozšířené reprezentace parametru.

Vyhledejte v dokumentaci k programovacímu jazyku, zda kompilátor provádí implicitní převody pro rozšíření číselných typů. Další informace naleznete v tématu Převodní tabulky typů.

Přesnost v porovnáních

Metoda Equals by měla být použita s opatrností, protože dvě zdánlivě ekvivalentní hodnoty mohou být nerovné kvůli rozdílné přesnosti těchto dvou hodnot. Následující příklad hlásí, že Single hodnota 0,3333 a Single vrácené vydělením 1 a 3 jsou nerovné.

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

Jedna metoda porovnání, která zabraňuje problémům spojeným s porovnáním rovnosti, zahrnuje definování přijatelného okraje rozdílu mezi dvěma hodnotami (například 01 % jedné z hodnot). Pokud je absolutní hodnota rozdílu mezi těmito dvěma hodnotami menší nebo rovna okraji, je pravděpodobné, že rozdíl bude výsledkem rozdílů v přesnosti, a proto budou hodnoty pravděpodobně stejné. Následující příklad používá tuto techniku k porovnání .333333 a 1/3, což jsou dvě Single hodnoty, které předchozí příklad kódu zjistil, že je nerovný.

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

V tomto případě jsou hodnoty stejné.

Poznámka:

Protože Epsilon definuje minimální výraz kladné hodnoty, jehož rozsah je téměř nula, musí být okraj rozdílu větší než Epsilon. Obvykle je to mnohokrát větší než Epsilon. Proto doporučujeme, abyste při porovnávání Double hodnot rovnosti nepoužívaliEpsilon.

Druhá technika, která zabraňuje problémům spojeným s porovnáním rovnosti, zahrnuje porovnání rozdílu mezi dvěma čísly s plovoucí desetinou čárkou s určitou absolutní hodnotou. Pokud je rozdíl menší nebo roven této absolutní hodnotě, jsou čísla rovna. Pokud je větší, čísla se nerovnají. Jedním ze způsobů, jak to udělat, je libovolně vybrat absolutní hodnotu. Je to ale problematické, protože přijatelná marže rozdílu závisí na velikosti Single hodnot. Druhý způsob využívá funkci návrhu formátu s plovoucí desetinou čárkou: Rozdíl mezi komponentami mantisy v celočíselné reprezentaci dvou hodnot s plovoucí desetinou čárkou označuje počet možných hodnot s plovoucí desetinou čárkou, které tyto dvě hodnoty odděluje. Například rozdíl mezi 0,0 a Epsilon 1, protože Epsilon je nejmenší reprezentovatelná hodnota při práci s Single hodnotou, jejíž hodnota je nula. Následující příklad používá tuto techniku k porovnání .333333 a 1/3, což jsou dvě Double hodnoty, které předchozí příklad kódu s metodou Equals(Single) , která byla nalezena jako nerovná. Všimněte si, že v příkladu BitConverter.GetBytes se používají metody BitConverter.ToInt32 k převodu hodnoty s plovoucí desetinnou čárkou s jednoduchou přesností na celočíselnou reprezentaci.

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

Přesnost čísel s plovoucí desetinnou čárkou nad rámec zdokumentované přesnosti je specifická pro implementaci a verzi rozhraní .NET. V důsledku toho může porovnání dvou čísel vést k různým výsledkům v závislosti na verzi rozhraní .NET, protože přesnost interní reprezentace čísel se může změnit.