Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Metoden Double.Equals(Double) implementerar System.IEquatable<T> gränssnittet och fungerar något bättre än Double.Equals(Object) eftersom den inte behöver konvertera parametern obj till ett objekt.
Bredda konverteringar
Beroende på programmeringsspråket kan det vara möjligt att koda en Equals metod där parametertypen har färre bitar (är smalare) än instanstypen. Detta är möjligt eftersom vissa programmeringsspråk utför en implicit breddningskonvertering som representerar parametern som en typ med så många bitar som instansen.
Anta till exempel att instanstypen är Double och att parametertypen är Int32. Microsoft C#-kompilatorn genererar instruktioner för att representera värdet för parametern som ett Double objekt och genererar sedan en Double.Equals(Double) metod som jämför instansens värden och den utvidgade representationen av parametern.
Läs dokumentationen för programmeringsspråket för att avgöra om kompilatorn utför implicita breddningskonverteringar av numeriska typer. Mer information finns i avsnittet Typkonverteringstabeller .
Precision i jämförelser
Metoden Equals bör användas med försiktighet, eftersom två till synes likvärdiga värden kan vara ojämlika på grund av de två värdenas olika precision. Följande exempel rapporterar att Double värdet .333333 och värdet Double som returneras genom att dividera 1 med 3 är olika.
// 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
I stället för att jämföra för likhet innebär en teknik att definiera en acceptabel relativ skillnadsmarginal mellan två värden (till exempel .001 % av ett av värdena). Om det absoluta värdet för skillnaden mellan de två värdena är mindre än eller lika med den marginalen beror skillnaden sannolikt på skillnader i precision och därför är värdena sannolikt lika. I följande exempel används den här tekniken för att jämföra .33333 och 1/3, de två Double värden som det tidigare kodexemplet visade sig vara ojämlika. I det här fallet är värdena lika.
// 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
Anteckning
Eftersom Epsilon definierar det minsta uttrycket för ett positivt värde vars intervall är nära noll måste skillnaden mellan två liknande värden vara större än Epsilon. Vanligtvis är den många gånger större än Epsilon. Därför rekommenderar vi att du inte använder när du jämför Epsilon värden för likhet.
En andra teknik innebär att jämföra skillnaden mellan två flyttalsnummer med något absolut värde. Om skillnaden är mindre än eller lika med det absoluta värdet är talen lika med. Om den är större är siffrorna inte lika. Ett alternativ är att godtyckligt välja ett absolut värde. Det är dock problematiskt eftersom en acceptabel skillnadsmarginal beror på värdenas Double storlek. Ett andra alternativ drar nytta av en designfunktion i flyttalsformatet: Skillnaden mellan heltalsrepresentationen av två flyttalsvärden anger antalet möjliga flyttalsvärden som separerar dem. Till exempel är skillnaden mellan 0,0 och Epsilon 1, eftersom Epsilon är det minsta representerande värdet när du arbetar med en Double vars värde är noll. I följande exempel används den här tekniken för att jämföra .33333 och 1/3, vilket är de två Double värden som det tidigare kodexemplet med Equals(Double) metoden visade sig vara ojämlika. I exemplet används metoden BitConverter.DoubleToInt64Bits för att konvertera ett flyttal med dubbel precision till dess heltalsrepresentation. I exemplet deklareras värdena som lika om det inte finns några möjliga flyttalsvärden mellan deras heltalsrepresentationer.
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
Anteckning
För vissa värden kan du betrakta dem som lika även när det finns ett möjligt flyttalsvärde mellan heltalsrepresentationerna. Tänk till exempel på de dubbla värdena 0.39 och 1.69 - 1.3 (som beräknas som 0.3899999999999999). På en lågändian-dator är heltalsrepresentationerna av dessa värden 4600697235336603894 respektive 4600697235336603892. Skillnaden mellan heltalsvärdena är 2, vilket innebär att det finns ett möjligt flyttalsvärde mellan 0.39 och 1.69 - 1.3.
Versionsskillnader
Precisionen för flyttalsnummer utöver den dokumenterade precisionen är specifik för implementeringen och versionen av .NET. Därför kan en jämförelse av två specifika tal ändras mellan versioner av .NET eftersom precisionen i talens interna representation kan ändras.
Nan
Om två Double.NaN värden testas för likhet genom att anropa Equals metoden returnerar truemetoden . Men om två Double.NaN värden testas för likhet med hjälp av likhetsoperatorn returnerar falseoperatorn . När du vill avgöra om värdet för ett Double inte är ett tal (NaN) är ett alternativ att anropa IsNaN metoden.