Sdílet prostřednictvím


Metoda System.Double.Equals

Th Double.Equals(Double) metoda implementuje System.IEquatable<T> rozhraní a provádí mírně lépe, než Double.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 Double a typ parametru je Int32. Kompilátor Jazyka Microsoft C# generuje instrukce představující hodnotu parametru jako Double objekt a pak vygeneruje Double.Equals(Double) metodu, 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 odlišné přesnosti těchto dvou hodnot. Následující příklad hlásí, že Double hodnota .333333 a Double hodnota vrácená vydělením 1 a 3 jsou nerovné.

// Initialize two doubles with apparently identical values
double double1 = .33333;
double double2 = (double) 1/3;
// Compare them for equality
Console.WriteLine(double1.Equals(double2));    // displays false
// Initialize two doubles with apparently identical values
let double1 = 0.33333
let double2 = double (1 / 3)
// Compare them for equality
printfn $"{double1.Equals double2}"    // displays false
' Initialize two doubles with apparently identical values
Dim double1 As Double = .33333
Dim double2 As Double = 1/3
' Compare them for equality
Console.WriteLine(double1.Equals(double2))    ' displays False

Místo porovnání rovnosti se jedna technika týká definování přijatelného relativního okraje rozdílu mezi dvěma hodnotami (například 001 % jedné z hodnot). Pokud je absolutní hodnota rozdílu mezi těmito dvěma hodnotami menší nebo rovna okraji, bude rozdíl pravděpodobně způsoben rozdíly v přesnosti, a proto budou hodnoty pravděpodobně stejné. Následující příklad používá tuto techniku k porovnání 0,33333 a 1/3, dvě Double hodnoty, které předchozí příklad kódu zjistil, že je nerovný. V tomto případě jsou hodnoty stejné.

// Initialize two doubles with apparently identical values
double double1 = .333333;
double double2 = (double) 1/3;
// Define the tolerance for variation in their values
double difference = Math.Abs(double1 * .00001);

// Compare the values
// The output to the console indicates that the two values are equal
if (Math.Abs(double1 - double2) <= difference)
   Console.WriteLine("double1 and double2 are equal.");
else
   Console.WriteLine("double1 and double2 are unequal.");
// Initialize two doubles with apparently identical values
let double1 = 0.333333
let double2 = double (1 / 3)
// Define the tolerance for variation in their values
let difference = abs (double1 * 0.00001)

// Compare the values
// The output to the console indicates that the two values are equal
if abs (double1 - double2) <= difference then
    printfn "double1 and double2 are equal."
else
    printfn "double1 and double2 are unequal."
' Initialize two doubles with apparently identical values
Dim double1 As Double = .33333
Dim double2 As Double = 1/3
' Define the tolerance for variation in their values
Dim difference As Double = Math.Abs(double1 * .00001)

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

Poznámka:

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

Druhá technika 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í. Jednou z možností je libovolně vybrat absolutní hodnotu. To je ale problematické, protože přijatelná marže rozdílu závisí na velikosti Double hodnot. Druhá alternativa využívá funkci návrhu formátu s plovoucí desetinou čárkou: Rozdíl mezi celočíselnou reprezentací dvou hodnot s plovoucí desetinou čárkou označuje počet možných hodnot s plovoucí desetinou čárkou, které je odděluje. Například rozdíl mezi 0,0 a Epsilon 1, protože Epsilon je nejmenší reprezentovatelná hodnota při práci s Double 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(Double) , která byla nalezena jako nerovná. Příklad používá metodu BitConverter.DoubleToInt64Bits k převodu hodnoty s plovoucí desetinnou čárkou s dvojitou přesností na její celočíselnou reprezentaci. Příklad deklaruje hodnoty jako stejné, pokud mezi jejich celočíselnou reprezentací neexistují žádné možné hodnoty s plovoucí desetinou čárkou.

public static void Main()
{
    // Initialize the values.
    double value1 = .1 * 10;
    double value2 = 0;
    for (int ctr = 0; ctr < 10; ctr++)
        value2 += .1;

    Console.WriteLine($"{value1:R} = {value2:R}: " +
        $"{HasMinimalDifference(value1, value2, 1)}");
}

public static bool HasMinimalDifference(
    double value1,
    double value2,
    int allowableDifference
    )
{
    // Convert the double values to long values.
    long lValue1 = BitConverter.DoubleToInt64Bits(value1);
    long lValue2 = BitConverter.DoubleToInt64Bits(value2);

    // If the signs are different, return false except for +0 and -0.
    if ((lValue1 >> 63) != (lValue2 >> 63))
    {
        if (value1 == value2)
            return true;

        return false;
    }

    // Calculate the number of possible
    // floating-point values in the difference.
    long diff = Math.Abs(lValue1 - lValue2);

    if (diff <= allowableDifference)
        return true;

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

let hasMinimalDifference (value1: double) (value2: double) (units: int) =
    let lValue1 = BitConverter.DoubleToInt64Bits value1
    let lValue2 = BitConverter.DoubleToInt64Bits value2

    // If the signs are different, return false except for +0 and -0.
    if (lValue1 >>> 63) <> (lValue2 >>> 63) then
        value1 = value2
    else
        let diff = abs (lValue1 - lValue2)

        diff <= int64 units

let value1 = 0.1 * 10.
let mutable value2 = 0.
for _ = 0 to 9 do
    value2 <- value2 + 0.1

printfn $"{value1:R} = {value2:R}: {hasMinimalDifference value1 value2 1}"
                

// The example displays the following output:
//        1 = 0.99999999999999989: True
Module Example
   Public Sub Main()
      Dim value1 As Double = .1 * 10
      Dim value2 As Double = 0
      For ctr As Integer =  0 To 9
         value2 += .1
      Next
               
      Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,
                        HasMinimalDifference(value1, value2, 1))
   End Sub

   Public Function HasMinimalDifference(value1 As Double, value2 As Double, units As Integer) As Boolean
      Dim lValue1 As long =  BitConverter.DoubleToInt64Bits(value1)
      Dim lValue2 As long =  BitConverter.DoubleToInt64Bits(value2)
      
      ' If the signs are different, Return False except for +0 and -0.
      If ((lValue1 >> 63) <> (lValue2 >> 63)) Then
         If value1 = value2 Then
            Return True
         End If           
         Return False
      End If

      Dim diff As Long =  Math.Abs(lValue1 - lValue2)

      If diff <= units Then
         Return True
      End If

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

Poznámka:

Uněkterýchch Představte si například dvojité hodnoty 0.39 a 1.69 - 1.3 (které se vypočítají jako 0.3899999999999999). Na malém endovém počítači jsou 4600697235336603894 celočíselné reprezentace těchto hodnot a 4600697235336603892v uvedeném pořadí. Rozdíl mezi celočíselnou hodnotou je 2, což znamená, že existuje možná hodnota s plovoucí desetinou čárkou mezi 0.39 a 1.69 - 1.3.

Rozdíly ve verzích

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 se může mezi verzemi .NET změnit porovnání dvou konkrétních čísel, protože se může změnit přesnost interní reprezentace čísel.

Není číslo

Pokud jsou dvě Double.NaN hodnoty testovány na rovnost voláním Equals metody, metoda vrátí true. Pokud jsou však dvě Double.NaN hodnoty testovány na rovnost pomocí operátoru rovnosti, vrátí falseoperátor . Chcete-li zjistit, zda hodnota Double není číslo (NaN), je alternativou volání IsNaN metody.