Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Megjegyzés:
Ez a cikk kiegészítő megjegyzéseket tartalmaz az API referenciadokumentációjához.
Az Double értéktípus egy dupla pontosságú 64 bites számot jelöl, amelynek értéke negatív 1,79769313486232e308 és pozitív 1,79769313486232e308, valamint pozitív vagy negatív nulla, PositiveInfinityNegativeInfinityés nem szám (NaN). Kifejezetten nagy értékeket (például bolygók vagy galaxisok közötti távolságokat) vagy rendkívül kicsiket (például egy anyag molekulatömegét kilogrammban) jelöl, és amelyek gyakran pontatlanok (például a földtől egy másik naprendszerig). A Double típus megfelel a bináris lebegőpontos számtani IEC 60559:1989 (IEEE 754) szabványnak.
Lebegőpontos ábrázolás és pontosság
Az Double adattípus a két pontosságú lebegőpontos értékeket 64 bites bináris formátumban tárolja, az alábbi táblázatban látható módon:
| Rész | Bitek |
|---|---|
| Matematikai alapszám vagy mantissza | 0-51 |
| Kitevő | 52-62 |
| Jel (0 = Pozitív, 1 = Negatív) | 63 |
Ahogyan a tizedes törtek nem képesek pontosan képviselni bizonyos törtértékeket (például 1/3 vagy Math.PI), a bináris törtek nem képesek bizonyos törtértékeket képviselni. Az 1/10 például, amelyet pontosan .1 tizedes törtként jelöl, a .001100110011 bináris törtként jelenik meg, a "0011" minta pedig a végtelenbe ismétlődik. Ebben az esetben a lebegőpontos érték nem pontos képet ad az általa képviselt számról. Az eredeti lebegőpontos értéken végzett további matematikai műveletek gyakran növelik a pontosság hiányát. Ha például összehasonlítja az .1 és 10 szorzás eredményét, és 0,1-et 9-szer ad hozzá, akkor láthatja, hogy az összeadás, mivel nyolc további műveletet is érintett, a kevésbé pontos eredményt eredményezte. (A 10-es .NET előtt ez az eltérés csak akkor jelenik meg, ha a két Double értéket az "R" standard numerikus formátumú sztring használatával jeleníti meg, amely a Double típus által támogatott 17 számjegyig jeleníti meg a pontosságot.)
using System;
public class Example13
{
public static void Main()
{
Double value = .1;
Double result1 = value * 10;
Double result2 = 0;
for (int ctr = 1; ctr <= 10; ctr++)
result2 += value;
Console.WriteLine($".1 * 10: {result1:R}");
Console.WriteLine($".1 Added 10 times: {result2:R}");
}
}
// The example displays the following output:
// .1 * 10: 1
// .1 Added 10 times: 0.99999999999999989
let value = 0.1
let result1 = value * 10.
let mutable result2 = 0.
for i = 1 to 10 do
result2 <- result2 + value
printfn $".1 * 10: {result1:R}"
printfn $".1 Added 10 times: {result2:R}"
// The example displays the following output:
// .1 * 10: 1
// .1 Added 10 times: 0.99999999999999989
Module Example14
Public Sub Main()
Dim value As Double = 0.1
Dim result1 As Double = value * 10
Dim result2 As Double
For ctr As Integer = 1 To 10
result2 += value
Next
Console.WriteLine(".1 * 10: {0:R}", result1)
Console.WriteLine(".1 Added 10 times: {0:R}", result2)
End Sub
End Module
' The example displays the following output:
' .1 * 10: 1
' .1 Added 10 times: 0.99999999999999989
Mivel egyes számok nem jeleníthetők meg pontosan tört bináris értékekként, a lebegőpontos számok csak a valós számokat tudják megközelíteni.
Az összes lebegőpontos szám is korlátozott számú jelentős számjegyből áll, ami azt is meghatározza, hogy egy lebegőpontos érték mennyire közelíti meg a valós számot. Egy Double érték legfeljebb 15 tizedesjegy pontossággal rendelkezik, de belsőleg legfeljebb 17 számjegy tartható fenn. Ez azt jelenti, hogy egyes lebegőpontos műveletek nem feltétlenül rendelkeznek a lebegőpontos értékek módosításának pontosságával. Az alábbi példa egy illusztrációt tartalmaz. Egy nagyon nagy lebegőpontos értéket határoz meg, majd hozzáadja a Double.Epsilon szorzatát és egy kvadrilliót. A termék azonban túl kicsi az eredeti lebegőpontos érték módosításához. Legkisebb számjegye ezred, míg a termék legjelentősebb számjegye a 10-309.
using System;
public class Example14
{
public static void Main()
{
Double value = 123456789012.34567;
Double additional = Double.Epsilon * 1e15;
Console.WriteLine($"{value} + {additional} = {value + additional}");
}
}
// The example displays the following output:
// 123456789012.346 + 4.94065645841247E-309 = 123456789012.346
open System
let value = 123456789012.34567
let additional = Double.Epsilon * 1e15
printfn $"{value} + {additional} = {value + additional}"
// The example displays the following output:
// 123456789012.346 + 4.94065645841247E-309 = 123456789012.346
Module Example15
Public Sub Main()
Dim value As Double = 123456789012.34567
Dim additional As Double = Double.Epsilon * 1.0E+15
Console.WriteLine("{0} + {1} = {2}", value, additional,
value + additional)
End Sub
End Module
' The example displays the following output:
' 123456789012.346 + 4.94065645841247E-309 = 123456789012.346
A lebegőpontos számok korlátozott pontossága számos következménnyel jár:
Előfordulhat, hogy két lebegőpontos szám, amely egy adott pontosságnak felel meg, nem hasonlítható össze egyenlővel, mivel a legkisebb jelentős számjegyeik eltérőek. Az alábbi példában egy számsort adunk össze, és az összegük a várt összeggel lesz összehasonlítva.
using System; public class Example10 { public static void Main() { Double[] values = { 10.0, 2.88, 2.88, 2.88, 9.0 }; Double result = 27.64; Double total = 0; foreach (var value in values) total += value; if (total.Equals(result)) Console.WriteLine("The sum of the values equals the total."); else Console.WriteLine($"The sum of the values ({total}) does not equal the total ({result})."); } } // The example displays the following output: // The sum of the values (36.64) does not equal the total (36.64). // // If the index items in the Console.WriteLine statement are changed to {0:R}, // the example displays the following output: // The sum of the values (27.639999999999997) does not equal the total (27.64).let values = [ 10.0; 2.88; 2.88; 2.88; 9.0 ] let result = 27.64 let total = List.sum values if total.Equals result then printfn "The sum of the values equals the total." else printfn $"The sum of the values ({total}) does not equal the total ({result})." // The example displays the following output: // The sum of the values (36.64) does not equal the total (36.64). // // If the index items in the Console.WriteLine statement are changed to {0:R}, // the example displays the following output: // The sum of the values (27.639999999999997) does not equal the total (27.64).Module Example11 Public Sub Main() Dim values() As Double = {10.0, 2.88, 2.88, 2.88, 9.0} Dim result As Double = 27.64 Dim total As Double For Each value In values total += value Next If total.Equals(result) Then Console.WriteLine("The sum of the values equals the total.") Else Console.WriteLine("The sum of the values ({0}) does not equal the total ({1}).", total, result) End If End Sub End Module ' The example displays the following output: ' The sum of the values (36.64) does not equal the total (36.64). ' ' If the index items in the Console.WriteLine statement are changed to {0:R}, ' the example displays the following output: ' The sum of the values (27.639999999999997) does not equal the total (27.64).A két érték egyenlőtlen, mert az összeadási műveletek során a pontosság elvész. Ebben az esetben a probléma megoldható úgy, hogy az összehasonlítás végrehajtása előtt meghívja a Math.Round(Double, Int32) metódust az Double értékek kívánt pontosságú kerekítésére.
Előfordulhat, hogy egy lebegőpontos számot használó matematikai vagy összehasonlító művelet nem ugyanazt az eredményt adja, ha decimális számot használ, mert a bináris lebegőpontos szám nem feltétlenül egyenlő a decimális számmal. Ezt egy korábbi példa szemlélteti a .1 és 10 szorzat eredményének megjelenítésével, majd a .1-szeres összeadással.
Amikor a törtértékekkel rendelkező numerikus műveletek pontossága fontos, a Double típus helyett a Decimal típust használhatja. Ha a típustartományon túli integrálértékekkel rendelkező numerikus műveletek pontossága Int128UInt128 fontos, használja a típust BigInteger .
Ha lebegőpontos számról van szó, előfordulhat, hogy az érték nem visszaalakítható. Egy értéket körüljárónak nevezünk, ha egy művelet az eredeti lebegőpontos számot átalakítja egy másik formába, egy inverz művelet visszaalakítja az átalakított formát egy lebegőpontos számmá, és a végső lebegőpontos szám megegyezik az eredeti lebegőpontos számmal. A körút sikertelen lehet, mert az átalakítás során egy vagy több legkevésbé jelentős számjegy elveszik vagy megváltozik.
Az alábbi példában három Double érték lesz sztringekké konvertálva, és egy fájlba mentve. Ha ezt a példát .NET-keretrendszerben futtatja, annak ellenére, hogy az értékek azonosnak tűnnek, a visszaállított értékek nem egyeznek az eredeti értékekkel. (Ezt azóta javították a .NET-ben, ahol az értékek helyesen körbeutaznak.)
StreamWriter sw = new(@"./Doubles.dat"); double[] values = [2.2 / 1.01, 1.0 / 3, Math.PI]; for (int ctr = 0; ctr < values.Length; ctr++) { sw.Write(values[ctr].ToString()); if (ctr != values.Length - 1) sw.Write("|"); } sw.Close(); double[] restoredValues = new double[values.Length]; StreamReader sr = new(@"./Doubles.dat"); string temp = sr.ReadToEnd(); string[] tempStrings = temp.Split('|'); for (int ctr = 0; ctr < tempStrings.Length; ctr++) restoredValues[ctr] = double.Parse(tempStrings[ctr]); for (int ctr = 0; ctr < values.Length; ctr++) Console.WriteLine($"{values[ctr]} {(values[ctr].Equals(restoredValues[ctr]) ? "=" : "<>")} {restoredValues[ctr]}"); // For .NET Framework only, the example displays the following output: // 2.17821782178218 <> 2.17821782178218 // 0.333333333333333 <> 0.333333333333333 // 3.14159265358979 <> 3.14159265358979open System open System.IO let values = [ 2.2 / 1.01; 1. / 3.; Math.PI ] using (new StreamWriter(@".\Doubles.dat")) (fun sw -> for i = 0 to values.Length - 1 do sw.Write(string values[i]) if i <> values.Length - 1 then sw.Write "|") using (new StreamReader(@".\Doubles.dat")) (fun sr -> let temp = sr.ReadToEnd() let tempStrings = temp.Split '|' let restoredValues = [ for i = 0 to tempStrings.Length - 1 do Double.Parse tempStrings[i] ] for i = 0 to values.Length - 1 do printfn $"""{values[i]} {if values[ i ].Equals restoredValues[i] then "=" else "<>"} {restoredValues[i]}""") // The example displays the following output: // 2.17821782178218 <> 2.17821782178218 // 0.333333333333333 <> 0.333333333333333 // 3.14159265358979 <> 3.14159265358979Imports System.IO Module Example12 Public Sub Main() Dim sw As New StreamWriter(".\Doubles.dat") Dim values() As Double = {2.2 / 1.01, 1.0 / 3, Math.PI} For ctr As Integer = 0 To values.Length - 1 sw.Write(values(ctr).ToString()) If ctr <> values.Length - 1 Then sw.Write("|") Next sw.Close() Dim restoredValues(values.Length - 1) As Double Dim sr As New StreamReader(".\Doubles.dat") Dim temp As String = sr.ReadToEnd() Dim tempStrings() As String = temp.Split("|"c) For ctr As Integer = 0 To tempStrings.Length - 1 restoredValues(ctr) = Double.Parse(tempStrings(ctr)) Next For ctr As Integer = 0 To values.Length - 1 Console.WriteLine("{0} {2} {1}", values(ctr), restoredValues(ctr), If(values(ctr).Equals(restoredValues(ctr)), "=", "<>")) Next End Sub End Module ' The example displays the following output: ' 2.17821782178218 <> 2.17821782178218 ' 0.333333333333333 <> 0.333333333333333 ' 3.14159265358979 <> 3.14159265358979Ha .NET-keretrendszert céloz meg, az értékek a "G17" standard numerikus formátum sztring használatával állíthatók össze a Double értékek teljes pontosságának megőrzése érdekében.
Single az értékek pontossága kisebb, mint az Double értékeké. A Single érték, amelyet látszólag egyenértékűvé konvertálnak Double, gyakran nem egyenlő a Double értékkel a pontosságbeli különbségek miatt. Az alábbi példában az azonos osztási műveletek eredménye külön-külön a Double és a Single értékhez van rendelve. Miután az Single értéket egy Doubleértékre öntötte, a két érték összehasonlítása azt mutatja, hogy egyenlőtlenek.
using System; public class Example9 { public static void Main() { Double value1 = 1 / 3.0; Single sValue2 = 1 / 3.0f; Double value2 = (Double)sValue2; Console.WriteLine($"{value1:R} = {value2:R}: {value1.Equals(value2)}"); } } // The example displays the following output: // 0.33333333333333331 = 0.3333333432674408: Falseopen System let value1 = 1. / 3. let sValue2 = 1f /3f let value2 = double sValue2 printfn $"{value1:R} = {value2:R}: {value1.Equals value2}" // The example displays the following output: // 0.33333333333333331 = 0.3333333432674408: FalseModule Example10 Public Sub Main() Dim value1 As Double = 1 / 3 Dim sValue2 As Single = 1 / 3 Dim value2 As Double = CDbl(sValue2) Console.WriteLine("{0} = {1}: {2}", value1, value2, value1.Equals(value2)) End Sub End Module ' The example displays the following output: ' 0.33333333333333331 = 0.3333333432674408: FalseA probléma elkerülése érdekében használja az Double adattípus helyett Single , vagy használja a Round metódust úgy, hogy mindkét érték ugyanolyan pontosságú legyen.
Emellett az értékekkel végzett Double aritmetikai és hozzárendelési műveletek eredménye platformonként kissé eltérhet a típus pontosságának Double elvesztése miatt. A konstans Double érték hozzárendelésének eredménye például eltérhet a .NET 32 bites és 64 bites verzióiban. Az alábbi példa ezt a különbséget mutatja be, ha a -4.42330604244772E-305 literálérték és a -4.42330604244772E-305 érték egy változóhoz Double van rendelve. Vegye figyelembe, hogy ebben az esetben a Parse(String) módszer eredménye nem szenved a pontosság elvesztésétől.
double value = -4.42330604244772E-305;
double fromLiteral = -4.42330604244772E-305;
double fromVariable = value;
double fromParse = Double.Parse("-4.42330604244772E-305");
Console.WriteLine("Double value from literal: {0,29:R}", fromLiteral);
Console.WriteLine("Double value from variable: {0,28:R}", fromVariable);
Console.WriteLine("Double value from Parse method: {0,24:R}", fromParse);
// On 32-bit versions of the .NET Framework, the output is:
// Double value from literal: -4.42330604244772E-305
// Double value from variable: -4.42330604244772E-305
// Double value from Parse method: -4.42330604244772E-305
//
// On other versions of the .NET Framework, the output is:
// Double value from literal: -4.4233060424477198E-305
// Double value from variable: -4.4233060424477198E-305
// Double value from Parse method: -4.42330604244772E-305
let value = -4.42330604244772E-305
let fromLiteral = -4.42330604244772E-305
let fromVariable = value
let fromParse = Double.Parse "-4.42330604244772E-305"
printfn $"Double value from literal: {fromLiteral,29:R}"
printfn $"Double value from variable: {fromVariable,28:R}"
printfn $"Double value from Parse method: {fromParse,24:R}"
// On 32-bit versions of the .NET Framework, the output is:
// Double value from literal: -4.42330604244772E-305
// Double value from variable: -4.42330604244772E-305
// Double value from Parse method: -4.42330604244772E-305
//
// On other versions of the .NET Framework, the output is:
// Double value from literal: -4.4233060424477198E-305
// Double value from variable: -4.4233060424477198E-305
// Double value from Parse method: -4.42330604244772E-305
Dim value As Double = -4.4233060424477198E-305
Dim fromLiteral As Double = -4.4233060424477198E-305
Dim fromVariable As Double = value
Dim fromParse As Double = Double.Parse("-4.42330604244772E-305")
Console.WriteLine("Double value from literal: {0,29:R}", fromLiteral)
Console.WriteLine("Double value from variable: {0,28:R}", fromVariable)
Console.WriteLine("Double value from Parse method: {0,24:R}", fromParse)
' On 32-bit versions of the .NET Framework, the output is:
' Double value from literal: -4.42330604244772E-305
' Double value from variable: -4.42330604244772E-305
' Double value from Parse method: -4.42330604244772E-305
'
' On other versions of the .NET Framework, the output is:
' Double value from literal: -4.4233060424477198E-305
' Double value from variable: -4.4233060424477198E-305
' Double value from Parse method: -4.42330604244772E-305
Egyenlőség tesztelése
Az egyenlőséghez két Double értéknek azonos értékeket kell jelölnie. Az értékek közötti pontosságbeli különbségek, illetve az egy vagy mindkét érték pontosságának elvesztése miatt azonban a lebegőpontos értékek, amelyek várhatóan azonosak lesznek, gyakran egyenlőtlenek lesznek a legkisebb jelentős számjegyeik különbségei miatt. Ennek eredményeként meghívja a Equals metódust annak megállapítására, hogy két érték egyenlő-e, vagy a metódus meghívása CompareTo a két Double érték közötti kapcsolat meghatározására, gyakran váratlan eredményeket eredményez. Ez nyilvánvaló a következő példában, ahol két látszólag egyenlő Double érték nem egyenlőnek bizonyul, mert az első 15 számjegy pontosságú, míg a második 17 számjegy pontosságú.
using System;
public class Example
{
public static void Main()
{
double value1 = .333333333333333;
double value2 = 1.0/3;
Console.WriteLine($"{value1} = {value2}: {value1.Equals(value2)}");
}
}
// The example displays the following output:
// 0.333333333333333 = 0.33333333333333331: False
open System
let value1 = 0.333333333333333
let value2 = 1. / 3.
printfn $"{value1} = {value2}: {value1.Equals value2}"
// The example displays the following output:
// 0.333333333333333 = 0.33333333333333331: False
Module Example1
Public Sub Main()
Dim value1 As Double = 0.333333333333333
Dim value2 As Double = 1 / 3
Console.WriteLine("{0} = {1}: {2}", value1, value2, value1.Equals(value2))
End Sub
End Module
' The example displays the following output:
' 0.333333333333333 = 0.33333333333333331: False
Azokban az esetekben, amikor a pontosság elvesztése valószínűleg hatással van az összehasonlítás eredményére, a következő alternatívák bármelyikét alkalmazhatja a Equals metódus meghívására CompareTo :
Hívja meg a Math.Round metódust, hogy mindkét érték azonos pontosságú legyen. Az alábbi példa módosít egy korábbi példát, hogy ezt a megközelítést használja, hogy két törtérték egyenértékű legyen.
double value1 = .333333333333333; double value2 = 1.0 / 3; int precision = 7; value1 = Math.Round(value1, precision); value2 = Math.Round(value2, precision); Console.WriteLine($"{value1} = {value2}: {value1.Equals(value2)}"); // The example displays the following output: // 0.3333333 = 0.3333333: Trueopen System let v1 = 0.333333333333333 let v2 = 1. / 3. let precision = 7 let value1 = Math.Round(v1, precision) let value2 = Math.Round(v2, precision) printfn $"{value1} = {value2}: {value1.Equals value2}" // The example displays the following output: // 0.3333333 = 0.3333333: TrueModule Example3 Public Sub Main() Dim value1 As Double = 0.333333333333333 Dim value2 As Double = 1 / 3 Dim precision As Integer = 7 value1 = Math.Round(value1, precision) value2 = Math.Round(value2, precision) Console.WriteLine("{0} = {1}: {2}", value1, value2, value1.Equals(value2)) End Sub End Module ' The example displays the following output: ' 0.3333333 = 0.3333333: TrueA pontosság problémája továbbra is a középponti értékek kerekítésére vonatkozik. További információkért tekintse meg a metódust Math.Round(Double, Int32, MidpointRounding) .
Tesztelje a közelítő egyenlőséget az egyenlőség helyett. Ehhez meg kell határoznia egy abszolút összeget, amellyel a két érték eltérhet, de egyenlő lehet, vagy meg kell határoznia egy relatív értéket, amellyel a kisebb érték eltérhet a nagyobb értéktől.
Figyelmeztetés
Double.Epsilon néha a két Double érték közötti távolság abszolút mértékeként használják az egyenlőség vizsgálatakor. Azonban Double.Epsilon a legkisebb lehetséges értéket méri, amely Double értékhez adható hozzá vagy vonható ki belőle, aminek az értéke nulla. A legtöbb pozitív és negatív Double érték esetében az érték Double.Epsilon túl kicsi ahhoz, hogy észlelhető legyen. Ezért a nulla értékek kivételével nem javasoljuk az egyenlőségi tesztekben való használatát.
Az alábbi példa az utóbbi megközelítést használja egy
IsApproximatelyEqualolyan módszer meghatározására, amely két érték közötti relatív különbséget teszteli. A módszer aMath.Max(value1, value2)-vel oszt, ezáltal az összehasonlítás a két érték közül a nagyobb (nagyság szerint) értékhez viszonyul, ami az eredményt a megfelelő nagyságrendre helyezi. HaMath.Maxnullát ad vissza (ami akkor fordul elő, ha az egyik érték nulla, a másik negatív), a metódus visszaesikMath.Min(value1, value2)a nem nulla érték osztóként való használatára. Az összehasonlítást azIsApproximatelyEqualmetódusra és a Equals(Double) metódusra intézett hívások eredményei is érintik.using System; public class Example3 { public static void Main() { double one1 = .1 * 10; double one2 = 0; for (int ctr = 1; ctr <= 10; ctr++) one2 += .1; Console.WriteLine($"{one1} = {one2}: {one1.Equals(one2)}"); Console.WriteLine($"{one1} is approximately equal to {one2}: {IsApproximatelyEqual(one1, one2, .000000001)}"); } static bool IsApproximatelyEqual(double value1, double value2, double epsilon) { // If they are equal anyway, just return True. if (value1.Equals(value2)) return true; // Handle NaN, Infinity. if (Double.IsInfinity(value1) | Double.IsNaN(value1)) return value1.Equals(value2); else if (Double.IsInfinity(value2) | Double.IsNaN(value2)) return value1.Equals(value2); // Handle zero to avoid division by zero double divisor = Math.Max(value1, value2); if (divisor.Equals(0)) divisor = Math.Min(value1, value2); return Math.Abs((value1 - value2) / divisor) <= epsilon; } } // The example displays the following output: // 1 = 0.99999999999999989: False // 1 is approximately equal to 0.99999999999999989: Trueopen System let isApproximatelyEqual (value1: double) (value2: double) (epsilon: double) = // If they are equal anyway, just return True. if value1.Equals value2 then true else // Handle NaN, Infinity. if Double.IsInfinity value1 || Double.IsNaN value1 then value1.Equals value2 elif Double.IsInfinity value2 || Double.IsNaN value2 then value1.Equals value2 else // Handle zero to avoid division by zero let divisor = max value1 value2 let divisor = if divisor.Equals 0 then min value1 value2 else divisor abs ((value1 - value2) / divisor) <= epsilon let one1 = 0.1 * 10. let mutable one2 = 0. for _ = 1 to 10 do one2 <- one2 + 0.1 printfn $"{one1} = {one2}: {one1.Equals one2}" printfn $"{one1} is approximately equal to {one2}: {isApproximatelyEqual one1 one2 0.000000001}" // The example displays the following output: // 1 = 0.99999999999999989: False // 1 is approximately equal to 0.99999999999999989: TrueModule Example4 Public Sub Main() Dim one1 As Double = 0.1 * 10 Dim one2 As Double = 0 For ctr As Integer = 1 To 10 one2 += 0.1 Next Console.WriteLine("{0} = {1}: {2}", one1, one2, one1.Equals(one2)) Console.WriteLine("{0} is approximately equal to {1}: {2}", one1, one2, IsApproximatelyEqual(one1, one2, 0.000000001)) End Sub Function IsApproximatelyEqual(value1 As Double, value2 As Double, epsilon As Double) As Boolean ' If they are equal anyway, just return True. If value1.Equals(value2) Then Return True ' Handle NaN, Infinity. If Double.IsInfinity(value1) Or Double.IsNaN(value1) Then Return value1.Equals(value2) ElseIf Double.IsInfinity(value2) Or Double.IsNaN(value2) Then Return value1.Equals(value2) End If ' Handle zero to avoid division by zero Dim divisor As Double = Math.Max(value1, value2) If divisor.Equals(0) Then divisor = Math.Min(value1, value2) End If Return Math.Abs((value1 - value2) / divisor) <= epsilon End Function End Module ' The example displays the following output: ' 1 = 0.99999999999999989: False ' 1 is approximately equal to 0.99999999999999989: True
Lebegőpontos értékek és kivételek
Az integrál típusú műveletektől eltérően, amelyek nullával való osztást vagy DivideByZeroException túlcsordulást eredményeznek OverflowException egy ellenőrzött környezetben, a lebegőpontos értékekkel végzett műveletek nem adnak kivételt. Ehelyett kivételes helyzetekben a lebegőpontos művelet eredménye nulla, pozitív végtelen, negatív végtelen vagy nem szám (NaN):
Ha egy lebegőpontos művelet eredménye túl kicsi a célformátumhoz, az eredmény nulla. Ez akkor fordulhat elő, ha két nagyon kis szám szorozva van, ahogy az alábbi példa is mutatja.
using System; public class Example6 { public static void Main() { Double value1 = 1.1632875981534209e-225; Double value2 = 9.1642346778e-175; Double result = value1 * value2; Console.WriteLine($"{value1} * {value2} = {result}"); Console.WriteLine($"{result} = 0: {result.Equals(0.0)}"); } } // The example displays the following output: // 1.16328759815342E-225 * 9.1642346778E-175 = 0 // 0 = 0: Truelet value1 = 1.1632875981534209e-225 let value2 = 9.1642346778e-175 let result = value1 * value2 printfn $"{value1} * {value2} = {result}" printfn $"{result} = 0: {result.Equals 0.0}" // The example displays the following output: // 1.16328759815342E-225 * 9.1642346778E-175 = 0 // 0 = 0: TrueModule Example7 Public Sub Main() Dim value1 As Double = 1.1632875981534209E-225 Dim value2 As Double = 9.1642346778E-175 Dim result As Double = value1 * value2 Console.WriteLine("{0} * {1} = {2}", value1, value2, result) Console.WriteLine("{0} = 0: {1}", result, result.Equals(0.0)) End Sub End Module ' The example displays the following output: ' 1.16328759815342E-225 * 9.1642346778E-175 = 0 ' 0 = 0: TrueHa egy lebegőpontos művelet eredményének nagysága meghaladja a célformátum tartományát, a művelet PositiveInfinity eredménye vagy NegativeInfinityaz eredmény előjelének megfelelő. A Double.MaxValue túlcsorduló művelet eredménye PositiveInfinity, és egy Double.MinValue túlcsorduló művelet eredménye NegativeInfinity, ahogy az alábbi példa mutatja.
using System; public class Example7 { public static void Main() { Double value1 = 4.565e153; Double value2 = 6.9375e172; Double result = value1 * value2; Console.WriteLine($"PositiveInfinity: {Double.IsPositiveInfinity(result)}"); Console.WriteLine($"NegativeInfinity: {Double.IsNegativeInfinity(result)}{Environment.NewLine}"); value1 = -value1; result = value1 * value2; Console.WriteLine($"PositiveInfinity: {Double.IsPositiveInfinity(result)}"); Console.WriteLine($"NegativeInfinity: {Double.IsNegativeInfinity(result)}"); } } // The example displays the following output: // PositiveInfinity: True // NegativeInfinity: False // // PositiveInfinity: False // NegativeInfinity: Trueopen System let value1 = 4.565e153 let value2 = 6.9375e172 let result = value1 * value2 printfn $"PositiveInfinity: {Double.IsPositiveInfinity result}" printfn $"NegativeInfinity: {Double.IsNegativeInfinity result}\n" let value3 = - value1 let result2 = value2 * value3 printfn $"PositiveInfinity: {Double.IsPositiveInfinity result2}" printfn $"NegativeInfinity: {Double.IsNegativeInfinity result2}" // The example displays the following output: // PositiveInfinity: True // NegativeInfinity: False // // PositiveInfinity: False // NegativeInfinity: TrueModule Example8 Public Sub Main() Dim value1 As Double = 4.565E+153 Dim value2 As Double = 6.9375E+172 Dim result As Double = value1 * value2 Console.WriteLine("PositiveInfinity: {0}", Double.IsPositiveInfinity(result)) Console.WriteLine("NegativeInfinity: {0}", Double.IsNegativeInfinity(result)) Console.WriteLine() value1 = -value1 result = value1 * value2 Console.WriteLine("PositiveInfinity: {0}", Double.IsPositiveInfinity(result)) Console.WriteLine("NegativeInfinity: {0}", Double.IsNegativeInfinity(result)) End Sub End Module ' The example displays the following output: ' PositiveInfinity: True ' NegativeInfinity: False ' ' PositiveInfinity: False ' NegativeInfinity: TruePositiveInfinity pozitív osztalékkal rendelkező nullával történő osztásból is származik, és NegativeInfinity negatív osztalékkal rendelkező nullával való osztás eredménye.
Ha egy lebegőpontos művelet érvénytelen, a művelet eredménye NaN. Például a NaN következő műveletek eredményei:
Osztás nullával nulla osztalékkal. Vegye figyelembe, hogy a nullával való osztás egyéb esetei vagy PositiveInfinity, vagy NegativeInfinity eredményeznek.
Bármely lebegőpontos művelet érvénytelen bemenettel. Ha például negatív értékkel hívja meg a Math.Sqrt metódust, az visszaadja NaN-t, ahogy a Math.Acos metódust is visszaadja NaN-t, ha egynél nagyobb vagy mínusz egynél kisebb értékkel hívják meg.
Bármely művelet, amelynek argumentuma értéke Double.NaN.
Típuskonverziók
A Double struktúra nem határoz meg explicit vagy implicit konverziós operátorokat, ehelyett a fordításokat a fordító implementálja.
Bármely primitív numerikus típus Double értékének konvertálása szélesítő átalakítás, ezért nem igényel explicit öntött operátort vagy átalakítási metódus meghívását, kivéve, ha egy fordító kifejezetten megköveteli. A C#-fordítóhoz például egy casting operátorra van szükség a Decimal és Double közötti átalakításhoz, míg a Visual Basic fordító nem. Az alábbi példa más primitív numerikus típusok minimális vagy maximális értékét konvertálja Double értékre.
dynamic[] values = { Byte.MinValue, Byte.MaxValue, Decimal.MinValue,
Decimal.MaxValue, Int16.MinValue, Int16.MaxValue,
Int32.MinValue, Int32.MaxValue, Int64.MinValue,
Int64.MaxValue, SByte.MinValue, SByte.MaxValue,
Single.MinValue, Single.MaxValue, UInt16.MinValue,
UInt16.MaxValue, UInt32.MinValue, UInt32.MaxValue,
UInt64.MinValue, UInt64.MaxValue };
double dblValue;
foreach (dynamic value in values)
{
if (value.GetType() == typeof(decimal))
dblValue = (double)value;
else
dblValue = value;
Console.WriteLine($"{value} ({value.GetType().Name}) --> " +
$"{dblValue:R} ({dblValue.GetType().Name})");
}
// The example displays the following output:
// 0 (Byte) --> 0 (Double)
// 255 (Byte) --> 255 (Double)
// -79228162514264337593543950335 (Decimal) --> -7.9228162514264338E+28 (Double)
// 79228162514264337593543950335 (Decimal) --> 7.9228162514264338E+28 (Double)
// -32768 (Int16) --> -32768 (Double)
// 32767 (Int16) --> 32767 (Double)
// -2147483648 (Int32) --> -2147483648 (Double)
// 2147483647 (Int32) --> 2147483647 (Double)
// -9223372036854775808 (Int64) --> -9.2233720368547758E+18 (Double)
// 9223372036854775807 (Int64) --> 9.2233720368547758E+18 (Double)
// -128 (SByte) --> -128 (Double)
// 127 (SByte) --> 127 (Double)
// -3.402823E+38 (Single) --> -3.4028234663852886E+38 (Double)
// 3.402823E+38 (Single) --> 3.4028234663852886E+38 (Double)
// 0 (UInt16) --> 0 (Double)
// 65535 (UInt16) --> 65535 (Double)
// 0 (UInt32) --> 0 (Double)
// 4294967295 (UInt32) --> 4294967295 (Double)
// 0 (UInt64) --> 0 (Double)
// 18446744073709551615 (UInt64) --> 1.8446744073709552E+19 (Double)
open System
let values: obj[] =
[| Byte.MinValue; Byte.MaxValue; Decimal.MinValue
Decimal.MaxValue; Int16.MinValue; Int16.MaxValue
Int32.MinValue; Int32.MaxValue; Int64.MinValue
Int64.MaxValue; SByte.MinValue; SByte.MaxValue
Single.MinValue; Single.MaxValue; UInt16.MinValue
UInt16.MaxValue; UInt32.MinValue, UInt32.MaxValue
UInt64.MinValue; UInt64.MaxValue |]
for value in values do
let dblValue = value :?> double
printfn $"{value} ({value.GetType().Name}) --> {dblValue:R} ({dblValue.GetType().Name})"
// The example displays the following output:
// 0 (Byte) --> 0 (Double)
// 255 (Byte) --> 255 (Double)
// -79228162514264337593543950335 (Decimal) --> -7.9228162514264338E+28 (Double)
// 79228162514264337593543950335 (Decimal) --> 7.9228162514264338E+28 (Double)
// -32768 (Int16) --> -32768 (Double)
// 32767 (Int16) --> 32767 (Double)
// -2147483648 (Int32) --> -2147483648 (Double)
// 2147483647 (Int32) --> 2147483647 (Double)
// -9223372036854775808 (Int64) --> -9.2233720368547758E+18 (Double)
// 9223372036854775807 (Int64) --> 9.2233720368547758E+18 (Double)
// -128 (SByte) --> -128 (Double)
// 127 (SByte) --> 127 (Double)
// -3.402823E+38 (Single) --> -3.4028234663852886E+38 (Double)
// 3.402823E+38 (Single) --> 3.4028234663852886E+38 (Double)
// 0 (UInt16) --> 0 (Double)
// 65535 (UInt16) --> 65535 (Double)
// 0 (UInt32) --> 0 (Double)
// 4294967295 (UInt32) --> 4294967295 (Double)
// 0 (UInt64) --> 0 (Double)
// 18446744073709551615 (UInt64) --> 1.8446744073709552E+19 (Double)
Module Example5
Public Sub Main()
Dim values() As Object = {Byte.MinValue, Byte.MaxValue, Decimal.MinValue,
Decimal.MaxValue, Int16.MinValue, Int16.MaxValue,
Int32.MinValue, Int32.MaxValue, Int64.MinValue,
Int64.MaxValue, SByte.MinValue, SByte.MaxValue,
Single.MinValue, Single.MaxValue, UInt16.MinValue,
UInt16.MaxValue, UInt32.MinValue, UInt32.MaxValue,
UInt64.MinValue, UInt64.MaxValue}
Dim dblValue As Double
For Each value In values
dblValue = value
Console.WriteLine("{0} ({1}) --> {2:R} ({3})",
value, value.GetType().Name,
dblValue, dblValue.GetType().Name)
Next
End Sub
End Module
' The example displays the following output:
' 0 (Byte) --> 0 (Double)
' 255 (Byte) --> 255 (Double)
' -79228162514264337593543950335 (Decimal) --> -7.9228162514264338E+28 (Double)
' 79228162514264337593543950335 (Decimal) --> 7.9228162514264338E+28 (Double)
' -32768 (Int16) --> -32768 (Double)
' 32767 (Int16) --> 32767 (Double)
' -2147483648 (Int32) --> -2147483648 (Double)
' 2147483647 (Int32) --> 2147483647 (Double)
' -9223372036854775808 (Int64) --> -9.2233720368547758E+18 (Double)
' 9223372036854775807 (Int64) --> 9.2233720368547758E+18 (Double)
' -128 (SByte) --> -128 (Double)
' 127 (SByte) --> 127 (Double)
' -3.402823E+38 (Single) --> -3.4028234663852886E+38 (Double)
' 3.402823E+38 (Single) --> 3.4028234663852886E+38 (Double)
' 0 (UInt16) --> 0 (Double)
' 65535 (UInt16) --> 65535 (Double)
' 0 (UInt32) --> 0 (Double)
' 4294967295 (UInt32) --> 4294967295 (Double)
' 0 (UInt64) --> 0 (Double)
' 18446744073709551615 (UInt64) --> 1.8446744073709552E+19 (Double)
Ezenkívül a Single, Single.NaN és Single.PositiveInfinity értékek rendre Single.NegativeInfinity, Double.NaN és Double.PositiveInfinity-ra alakulnak át.
Vegye figyelembe, hogy bizonyos numerikus típusok értékké Double alakítása a pontosság elvesztésével járhat. Ahogy a példa is mutatja, a pontosságvesztés lehetséges, amikor a Decimal, Int64 és UInt64 értékeket Double értékekké alakítjuk.
A Double érték konvertálása bármely más primitív numerikus adattípus értékére szűkítő konverzió, amelyhez öntött operátor (C#-ban), átalakítási módszerre (Visual Basic) vagy Convert metódus hívására van szükség. A céladattípus tartományán kívül eső értékek, amelyeket a céltípus MinValue és MaxValue a tulajdonságok határoznak meg, az alábbi táblázatban látható módon viselkednek.
| Céltípus | Eredmény |
|---|---|
| Bármilyen integráltípus | Kivétel OverflowException , ha az átalakítás ellenőrzött környezetben történik. Ha az átalakítás nem ellenőrzött környezetben történik (az alapértelmezett C#-ban), a konvertálási művelet sikeres, de az érték túlcsordul. |
| Decimal | Kivétel OverflowException. |
| Single |
Single.NegativeInfinity negatív értékek esetén. Single.PositiveInfinity pozitív értékek esetén. |
Ezen kívül Double.NaN, Double.PositiveInfinity és Double.NegativeInfinity egy OverflowException-t dob az egész számokká történő konvertáláskor egy ellenőrzött környezetben, de ezek az értékek túlcsordulnak, amikor nem ellenőrzött környezetben alakulnak egész számokká. A Decimal konvertálásánál mindig dob egy OverflowException. A konvertálás során Single-ra, Single.NaN-re, Single.PositiveInfinity-re és Single.NegativeInfinity-ra konvertálnak.
A pontosság elvesztését okozhatja, ha egy Double értéket más numerikus típussá alakít át. Az integráltípusok bármelyikére való konvertáláskor, a példából láthatóan, a törtrész elveszik, amikor a Double értéket kerekítik (mint a Visual Basic-ben) vagy csonkolják (mint a C#-ban). Konvertálások esetén Decimal és Single értékekre, előfordulhat, hogy az Double érték nem rendelkezik pontos ábrázolással a céladattípusban.
Az alábbi példa számos Double értéket konvertál több más numerikus típussá. A konvertálások egy ellenőrzött környezetben történnek Visual Basic (alapértelmezett), c# (a bejelölt kulcsszó miatt) és F# (a Checked modul miatt). A példában szereplő kimenet az eredményt mutatja mind az ellenőrzött, mind a nem ellenőrzött környezetben történő átalakításokra. A Visual Basic programozási nyelvben átalakításokat hajthat végre egy ellenőrzetlen környezetben a /removeintchecks+ fordítókapcsolóval történő fordítással, C#-ban a checked utasítás megjegyzésével, és F#-ban a open Checked utasítás megjegyzésével.
using System;
public class Example5
{
public static void Main()
{
Double[] values = { Double.MinValue, -67890.1234, -12345.6789,
12345.6789, 67890.1234, Double.MaxValue,
Double.NaN, Double.PositiveInfinity,
Double.NegativeInfinity };
checked
{
foreach (var value in values)
{
try
{
Int64 lValue = (long)value;
Console.WriteLine($"{value} ({value.GetType().Name}) --> {lValue} (0x{lValue:X16}) ({lValue.GetType().Name})");
}
catch (OverflowException)
{
Console.WriteLine($"Unable to convert {value} to Int64.");
}
try
{
UInt64 ulValue = (ulong)value;
Console.WriteLine($"{value} ({value.GetType().Name}) --> {ulValue} (0x{ulValue:X16}) ({ulValue.GetType().Name})");
}
catch (OverflowException)
{
Console.WriteLine($"Unable to convert {value} to UInt64.");
}
try
{
Decimal dValue = (decimal)value;
Console.WriteLine($"{value} ({value.GetType().Name}) --> {dValue} ({dValue.GetType().Name})");
}
catch (OverflowException)
{
Console.WriteLine($"Unable to convert {value} to Decimal.");
}
try
{
Single sValue = (float)value;
Console.WriteLine($"{value} ({value.GetType().Name}) --> {sValue} ({sValue.GetType().Name})");
}
catch (OverflowException)
{
Console.WriteLine($"Unable to convert {value} to Single.");
}
Console.WriteLine();
}
}
}
}
// The example displays the following output for conversions performed
// in a checked context:
// Unable to convert -1.79769313486232E+308 to Int64.
// Unable to convert -1.79769313486232E+308 to UInt64.
// Unable to convert -1.79769313486232E+308 to Decimal.
// -1.79769313486232E+308 (Double) --> -Infinity (Single)
//
// -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
// Unable to convert -67890.1234 to UInt64.
// -67890.1234 (Double) --> -67890.1234 (Decimal)
// -67890.1234 (Double) --> -67890.13 (Single)
//
// -12345.6789 (Double) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
// Unable to convert -12345.6789 to UInt64.
// -12345.6789 (Double) --> -12345.6789 (Decimal)
// -12345.6789 (Double) --> -12345.68 (Single)
//
// 12345.6789 (Double) --> 12345 (0x0000000000003039) (Int64)
// 12345.6789 (Double) --> 12345 (0x0000000000003039) (UInt64)
// 12345.6789 (Double) --> 12345.6789 (Decimal)
// 12345.6789 (Double) --> 12345.68 (Single)
//
// 67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
// 67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
// 67890.1234 (Double) --> 67890.1234 (Decimal)
// 67890.1234 (Double) --> 67890.13 (Single)
//
// Unable to convert 1.79769313486232E+308 to Int64.
// Unable to convert 1.79769313486232E+308 to UInt64.
// Unable to convert 1.79769313486232E+308 to Decimal.
// 1.79769313486232E+308 (Double) --> Infinity (Single)
//
// Unable to convert NaN to Int64.
// Unable to convert NaN to UInt64.
// Unable to convert NaN to Decimal.
// NaN (Double) --> NaN (Single)
//
// Unable to convert Infinity to Int64.
// Unable to convert Infinity to UInt64.
// Unable to convert Infinity to Decimal.
// Infinity (Double) --> Infinity (Single)
//
// Unable to convert -Infinity to Int64.
// Unable to convert -Infinity to UInt64.
// Unable to convert -Infinity to Decimal.
// -Infinity (Double) --> -Infinity (Single)
// The example displays the following output for conversions performed
// in an unchecked context:
// -1.79769313486232E+308 (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
// -1.79769313486232E+308 (Double) --> 9223372036854775808 (0x8000000000000000) (UInt64)
// Unable to convert -1.79769313486232E+308 to Decimal.
// -1.79769313486232E+308 (Double) --> -Infinity (Single)
//
// -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
// -67890.1234 (Double) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE) (UInt64)
// -67890.1234 (Double) --> -67890.1234 (Decimal)
// -67890.1234 (Double) --> -67890.13 (Single)
//
// -12345.6789 (Double) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
// -12345.6789 (Double) --> 18446744073709539271 (0xFFFFFFFFFFFFCFC7) (UInt64)
// -12345.6789 (Double) --> -12345.6789 (Decimal)
// -12345.6789 (Double) --> -12345.68 (Single)
//
// 12345.6789 (Double) --> 12345 (0x0000000000003039) (Int64)
// 12345.6789 (Double) --> 12345 (0x0000000000003039) (UInt64)
// 12345.6789 (Double) --> 12345.6789 (Decimal)
// 12345.6789 (Double) --> 12345.68 (Single)
//
// 67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
// 67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
// 67890.1234 (Double) --> 67890.1234 (Decimal)
// 67890.1234 (Double) --> 67890.13 (Single)
//
// 1.79769313486232E+308 (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
// 1.79769313486232E+308 (Double) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert 1.79769313486232E+308 to Decimal.
// 1.79769313486232E+308 (Double) --> Infinity (Single)
//
// NaN (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
// NaN (Double) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert NaN to Decimal.
// NaN (Double) --> NaN (Single)
//
// Infinity (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
// Infinity (Double) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert Infinity to Decimal.
// Infinity (Double) --> Infinity (Single)
//
// -Infinity (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
// -Infinity (Double) --> 9223372036854775808 (0x8000000000000000) (UInt64)
// Unable to convert -Infinity to Decimal.
// -Infinity (Double) --> -Infinity (Single)
open System
open Checked
let values =
[| Double.MinValue; -67890.1234; -12345.6789
12345.6789; 67890.1234; Double.MaxValue
Double.NaN; Double.PositiveInfinity;
Double.NegativeInfinity |]
for value in values do
try
let lValue = int64 value
printfn $"{value} ({value.GetType().Name}) --> {lValue} (0x{lValue:X16}) ({lValue.GetType().Name})"
with :? OverflowException ->
printfn $"Unable to convert {value} to Int64."
try
let ulValue = uint64 value
printfn $"{value} ({value.GetType().Name}) --> {ulValue} (0x{ulValue:X16}) ({ulValue.GetType().Name})"
with :? OverflowException ->
printfn $"Unable to convert {value} to UInt64."
try
let dValue = decimal value
printfn $"{value} ({value.GetType().Name}) --> {dValue} ({dValue.GetType().Name})"
with :? OverflowException ->
printfn $"Unable to convert {value} to Decimal."
try
let sValue = float32 value
printfn $"{value} ({value.GetType().Name}) --> {sValue} ({sValue.GetType().Name})"
with :? OverflowException ->
printfn $"Unable to convert {value} to Single."
printfn ""
// The example displays the following output for conversions performed
// in a checked context:
// Unable to convert -1.79769313486232E+308 to Int64.
// Unable to convert -1.79769313486232E+308 to UInt64.
// Unable to convert -1.79769313486232E+308 to Decimal.
// -1.79769313486232E+308 (Double) --> -Infinity (Single)
//
// -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
// Unable to convert -67890.1234 to UInt64.
// -67890.1234 (Double) --> -67890.1234 (Decimal)
// -67890.1234 (Double) --> -67890.13 (Single)
//
// -12345.6789 (Double) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
// Unable to convert -12345.6789 to UInt64.
// -12345.6789 (Double) --> -12345.6789 (Decimal)
// -12345.6789 (Double) --> -12345.68 (Single)
//
// 12345.6789 (Double) --> 12345 (0x0000000000003039) (Int64)
// 12345.6789 (Double) --> 12345 (0x0000000000003039) (UInt64)
// 12345.6789 (Double) --> 12345.6789 (Decimal)
// 12345.6789 (Double) --> 12345.68 (Single)
//
// 67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
// 67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
// 67890.1234 (Double) --> 67890.1234 (Decimal)
// 67890.1234 (Double) --> 67890.13 (Single)
//
// Unable to convert 1.79769313486232E+308 to Int64.
// Unable to convert 1.79769313486232E+308 to UInt64.
// Unable to convert 1.79769313486232E+308 to Decimal.
// 1.79769313486232E+308 (Double) --> Infinity (Single)
//
// Unable to convert NaN to Int64.
// Unable to convert NaN to UInt64.
// Unable to convert NaN to Decimal.
// NaN (Double) --> NaN (Single)
//
// Unable to convert Infinity to Int64.
// Unable to convert Infinity to UInt64.
// Unable to convert Infinity to Decimal.
// Infinity (Double) --> Infinity (Single)
//
// Unable to convert -Infinity to Int64.
// Unable to convert -Infinity to UInt64.
// Unable to convert -Infinity to Decimal.
// -Infinity (Double) --> -Infinity (Single)
// The example displays the following output for conversions performed
// in an unchecked context:
// -1.79769313486232E+308 (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
// -1.79769313486232E+308 (Double) --> 9223372036854775808 (0x8000000000000000) (UInt64)
// Unable to convert -1.79769313486232E+308 to Decimal.
// -1.79769313486232E+308 (Double) --> -Infinity (Single)
//
// -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
// -67890.1234 (Double) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE) (UInt64)
// -67890.1234 (Double) --> -67890.1234 (Decimal)
// -67890.1234 (Double) --> -67890.13 (Single)
//
// -12345.6789 (Double) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
// -12345.6789 (Double) --> 18446744073709539271 (0xFFFFFFFFFFFFCFC7) (UInt64)
// -12345.6789 (Double) --> -12345.6789 (Decimal)
// -12345.6789 (Double) --> -12345.68 (Single)
//
// 12345.6789 (Double) --> 12345 (0x0000000000003039) (Int64)
// 12345.6789 (Double) --> 12345 (0x0000000000003039) (UInt64)
// 12345.6789 (Double) --> 12345.6789 (Decimal)
// 12345.6789 (Double) --> 12345.68 (Single)
//
// 67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
// 67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
// 67890.1234 (Double) --> 67890.1234 (Decimal)
// 67890.1234 (Double) --> 67890.13 (Single)
//
// 1.79769313486232E+308 (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
// 1.79769313486232E+308 (Double) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert 1.79769313486232E+308 to Decimal.
// 1.79769313486232E+308 (Double) --> Infinity (Single)
//
// NaN (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
// NaN (Double) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert NaN to Decimal.
// NaN (Double) --> NaN (Single)
//
// Infinity (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
// Infinity (Double) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert Infinity to Decimal.
// Infinity (Double) --> Infinity (Single)
//
// -Infinity (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
// -Infinity (Double) --> 9223372036854775808 (0x8000000000000000) (UInt64)
// Unable to convert -Infinity to Decimal.
// -Infinity (Double) --> -Infinity (Single)
Module Example6
Public Sub Main()
Dim values() As Double = {Double.MinValue, -67890.1234, -12345.6789,
12345.6789, 67890.1234, Double.MaxValue,
Double.NaN, Double.PositiveInfinity,
Double.NegativeInfinity}
For Each value In values
Try
Dim lValue As Int64 = CLng(value)
Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
value, value.GetType().Name,
lValue, lValue.GetType().Name)
Catch e As OverflowException
Console.WriteLine("Unable to convert {0} to Int64.", value)
End Try
Try
Dim ulValue As UInt64 = CULng(value)
Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
value, value.GetType().Name,
ulValue, ulValue.GetType().Name)
Catch e As OverflowException
Console.WriteLine("Unable to convert {0} to UInt64.", value)
End Try
Try
Dim dValue As Decimal = CDec(value)
Console.WriteLine("{0} ({1}) --> {2} ({3})",
value, value.GetType().Name,
dValue, dValue.GetType().Name)
Catch e As OverflowException
Console.WriteLine("Unable to convert {0} to Decimal.", value)
End Try
Try
Dim sValue As Single = CSng(value)
Console.WriteLine("{0} ({1}) --> {2} ({3})",
value, value.GetType().Name,
sValue, sValue.GetType().Name)
Catch e As OverflowException
Console.WriteLine("Unable to convert {0} to Single.", value)
End Try
Console.WriteLine()
Next
End Sub
End Module
' The example displays the following output for conversions performed
' in a checked context:
' Unable to convert -1.79769313486232E+308 to Int64.
' Unable to convert -1.79769313486232E+308 to UInt64.
' Unable to convert -1.79769313486232E+308 to Decimal.
' -1.79769313486232E+308 (Double) --> -Infinity (Single)
'
' -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
' Unable to convert -67890.1234 to UInt64.
' -67890.1234 (Double) --> -67890.1234 (Decimal)
' -67890.1234 (Double) --> -67890.13 (Single)
'
' -12345.6789 (Double) --> -12346 (0xFFFFFFFFFFFFCFC6) (Int64)
' Unable to convert -12345.6789 to UInt64.
' -12345.6789 (Double) --> -12345.6789 (Decimal)
' -12345.6789 (Double) --> -12345.68 (Single)
'
' 12345.6789 (Double) --> 12346 (0x000000000000303A) (Int64)
' 12345.6789 (Double) --> 12346 (0x000000000000303A) (UInt64)
' 12345.6789 (Double) --> 12345.6789 (Decimal)
' 12345.6789 (Double) --> 12345.68 (Single)
'
' 67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
' 67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
' 67890.1234 (Double) --> 67890.1234 (Decimal)
' 67890.1234 (Double) --> 67890.13 (Single)
'
' Unable to convert 1.79769313486232E+308 to Int64.
' Unable to convert 1.79769313486232E+308 to UInt64.
' Unable to convert 1.79769313486232E+308 to Decimal.
' 1.79769313486232E+308 (Double) --> Infinity (Single)
'
' Unable to convert NaN to Int64.
' Unable to convert NaN to UInt64.
' Unable to convert NaN to Decimal.
' NaN (Double) --> NaN (Single)
'
' Unable to convert Infinity to Int64.
' Unable to convert Infinity to UInt64.
' Unable to convert Infinity to Decimal.
' Infinity (Double) --> Infinity (Single)
'
' Unable to convert -Infinity to Int64.
' Unable to convert -Infinity to UInt64.
' Unable to convert -Infinity to Decimal.
' -Infinity (Double) --> -Infinity (Single)
' The example displays the following output for conversions performed
' in an unchecked context:
' -1.79769313486232E+308 (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
' -1.79769313486232E+308 (Double) --> 9223372036854775808 (0x8000000000000000) (UInt64)
' Unable to convert -1.79769313486232E+308 to Decimal.
' -1.79769313486232E+308 (Double) --> -Infinity (Single)
'
' -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
' -67890.1234 (Double) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE) (UInt64)
' -67890.1234 (Double) --> -67890.1234 (Decimal)
' -67890.1234 (Double) --> -67890.13 (Single)
'
' -12345.6789 (Double) --> -12346 (0xFFFFFFFFFFFFCFC6) (Int64)
' -12345.6789 (Double) --> 18446744073709539270 (0xFFFFFFFFFFFFCFC6) (UInt64)
' -12345.6789 (Double) --> -12345.6789 (Decimal)
' -12345.6789 (Double) --> -12345.68 (Single)
'
' 12345.6789 (Double) --> 12346 (0x000000000000303A) (Int64)
' 12345.6789 (Double) --> 12346 (0x000000000000303A) (UInt64)
' 12345.6789 (Double) --> 12345.6789 (Decimal)
' 12345.6789 (Double) --> 12345.68 (Single)
'
' 67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
' 67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
' 67890.1234 (Double) --> 67890.1234 (Decimal)
' 67890.1234 (Double) --> 67890.13 (Single)
'
' 1.79769313486232E+308 (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
' 1.79769313486232E+308 (Double) --> 0 (0x0000000000000000) (UInt64)
' Unable to convert 1.79769313486232E+308 to Decimal.
' 1.79769313486232E+308 (Double) --> Infinity (Single)
'
' NaN (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
' NaN (Double) --> 0 (0x0000000000000000) (UInt64)
' Unable to convert NaN to Decimal.
' NaN (Double) --> NaN (Single)
'
' Infinity (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
' Infinity (Double) --> 0 (0x0000000000000000) (UInt64)
' Unable to convert Infinity to Decimal.
' Infinity (Double) --> Infinity (Single)
'
' -Infinity (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
' -Infinity (Double) --> 9223372036854775808 (0x8000000000000000) (UInt64)
' Unable to convert -Infinity to Decimal.
' -Infinity (Double) --> -Infinity (Single)
A numerikus típusok konvertálásáról további információt a Type Conversion in .NET és Type Conversion Tables című témakörben talál.
Lebegőpontos funkciók
A Double struktúra és a kapcsolódó típusok a következő területeken biztosítják a műveletek végrehajtásának módszereit:
Értékek összehasonlítása. Meghívhatja a Equals metódust annak meghatározására, hogy két Double érték egyenlő-e, vagy a CompareTo két érték közötti kapcsolatot meghatározó metódust.
A Double struktúra az összehasonlító operátorok teljes készletét is támogatja. Tesztelheti például az egyenlőséget vagy az egyenlőtlenséget, vagy meghatározhatja, hogy az egyik érték nagyobb-e, mint egy másik. Ha az operandusok egyike nem egy numerikus típus Double, akkor az összehasonlítás végrehajtása előtt a program átalakítja azt egy Double számmá.
Figyelmeztetés
A pontosságbeli különbségek miatt két Double érték, amelyeket egyenlőnek várnak, eltérőnek bizonyulhatnak, ami befolyásolja az összehasonlítás eredményét. A két Double érték összehasonlításával kapcsolatos információkért lásd az Egyenlőségi teszt szakaszt.
Meghívhatja a IsNaN, , IsInfinityIsPositiveInfinityés IsNegativeInfinity metódusokat is, hogy teszteljék ezeket a speciális értékeket.
Matematikai műveletek. A gyakori aritmetikai műveleteket, például az összeadást, kivonást, szorzást és osztást nem metódusokkal hajtják végre, hanem a nyelvi fordítók és a közös köztes nyelv (CIL) utasításai. Ha egy matematikai művelet egyik operandusa nem egy numerikus típus Double, akkor a művelet végrehajtása előtt a program átalakítja egy operandussá Double . A művelet eredménye is egy Double érték.
Más matematikai műveletek is elvégezhetők a
static(SharedVisual Basic) metódus meghívásával a System.Math osztályban. Ez magában foglalja az aritmetikai (például Math.Abs, Math.Signés ), geometria (például Math.Sqrt és Math.CosMath.Sin) és számításhoz (például Math.Log) gyakran használt további módszereket is.Egy Double értékben az egyes biteket is manipulálhatja. A BitConverter.DoubleToInt64Bits metódus megőrzi egy Double érték bitmintáját egy 64 bites egész számban. A BitConverter.GetBytes(Double) metódus visszaadja a bitmintáját egy bájttömbben.
Kerekítés. A kerekítést gyakran használják a lebegőpontos ábrázolás és pontosság problémái által okozott értékek közötti különbségek hatásának csökkentésére. A Double értéket a Math.Round függvény meghívásával kerekítheti.
Formázás. Az Double értékeket sztringként is megjelenítheti a ToString metódus meghívásával vagy az összetett formázási funkció használatával. Az információért arról, hogyan szabályozzák a formátum stringek a lebegőpontos értékek sztring reprezentációját, lásd: Standard Numeric Format Strings és Custom Numeric Format Strings.
Sztringek elemzése. A lebegőpontos érték sztringképét átalakíthatja Double értékké a Parse vagy a TryParse metódus meghívásával. Ha az elemzési művelet sikertelen, a Parse metódus kivételt ad vissza, míg a TryParse metódus ad vissza
false.Típuskonvertálás. A Double struktúra explicit felületi implementációt biztosít a IConvertible interfészhez, amely támogatja a két szabványos .NET adattípus közötti átalakítást. A fordítók támogatják a standard numerikus típusok minden más értékének implicit átalakítását Double értékekké. Bármely standard numerikus típus Double értékének konvertálása kibővítő átalakítás, és nem igényel öntvény operátort vagy átalakítási módszert.
Az értékek konvertálása Int64Single azonban a pontosság elvesztésével járhat. Az alábbi táblázat az alábbi típusok pontossági különbségeit sorolja fel:
Típus Maximális pontosság Belső pontosság Double 15 17 Int64 19 tizedesjegy 19 tizedesjegy Single 7 tizedesjegyek 9 tizedesjegy A pontosság problémája leggyakrabban az Single értékekből Double értékekké konvertált értékeket érinti. Az alábbi példában két azonos osztási művelet eredménye egyenlőtlen, mert az egyik egy egyszeres pontosságú lebegőpontos érték, amely átalakításra került egy Double típusú értékké.
using System; public class Example13 { public static void Main() { Double value = .1; Double result1 = value * 10; Double result2 = 0; for (int ctr = 1; ctr <= 10; ctr++) result2 += value; Console.WriteLine($".1 * 10: {result1:R}"); Console.WriteLine($".1 Added 10 times: {result2:R}"); } } // The example displays the following output: // .1 * 10: 1 // .1 Added 10 times: 0.99999999999999989let value = 0.1 let result1 = value * 10. let mutable result2 = 0. for i = 1 to 10 do result2 <- result2 + value printfn $".1 * 10: {result1:R}" printfn $".1 Added 10 times: {result2:R}" // The example displays the following output: // .1 * 10: 1 // .1 Added 10 times: 0.99999999999999989Module Example14 Public Sub Main() Dim value As Double = 0.1 Dim result1 As Double = value * 10 Dim result2 As Double For ctr As Integer = 1 To 10 result2 += value Next Console.WriteLine(".1 * 10: {0:R}", result1) Console.WriteLine(".1 Added 10 times: {0:R}", result2) End Sub End Module ' The example displays the following output: ' .1 * 10: 1 ' .1 Added 10 times: 0.99999999999999989
Példák
Az alábbi példakód a következők használatát szemlélteti Double:
// The Temperature class stores the temperature as a Double
// and delegates most of the functionality to the Double
// implementation.
public class Temperature : IComparable, IFormattable
{
// IComparable.CompareTo implementation.
public int CompareTo(object obj) {
if (obj == null) return 1;
Temperature temp = obj as Temperature;
if (obj != null)
return m_value.CompareTo(temp.m_value);
else
throw new ArgumentException("object is not a Temperature");
}
// IFormattable.ToString implementation.
public string ToString(string format, IFormatProvider provider) {
if( format != null ) {
if( format.Equals("F") ) {
return String.Format("{0}'F", this.Value.ToString());
}
if( format.Equals("C") ) {
return String.Format("{0}'C", this.Celsius.ToString());
}
}
return m_value.ToString(format, provider);
}
// Parses the temperature from a string in the form
// [ws][sign]digits['F|'C][ws]
public static Temperature Parse(string s, NumberStyles styles, IFormatProvider provider) {
Temperature temp = new Temperature();
if( s.TrimEnd(null).EndsWith("'F") ) {
temp.Value = Double.Parse( s.Remove(s.LastIndexOf('\''), 2), styles, provider);
}
else if( s.TrimEnd(null).EndsWith("'C") ) {
temp.Celsius = Double.Parse( s.Remove(s.LastIndexOf('\''), 2), styles, provider);
}
else {
temp.Value = Double.Parse(s, styles, provider);
}
return temp;
}
// The value holder
protected double m_value;
public double Value {
get {
return m_value;
}
set {
m_value = value;
}
}
public double Celsius {
get {
return (m_value-32.0)/1.8;
}
set {
m_value = 1.8*value+32.0;
}
}
}
// The Temperature class stores the temperature as a Double
// and delegates most of the functionality to the Double
// implementation.
type Temperature() =
member val Value = 0. with get, set
member this.Celsius
with get () = (this.Value - 32.) / 1.8
and set (value) =
this.Value <- 1.8 * value + 32.
// Parses the temperature from a string in the form
// [ws][sign]digits['F|'C][ws]
static member Parse(s: string, styles: NumberStyles, provider: IFormatProvider) =
let temp = Temperature()
if s.TrimEnd(null).EndsWith "'F" then
temp.Value <- Double.Parse(s.Remove(s.LastIndexOf '\'', 2), styles, provider)
elif s.TrimEnd(null).EndsWith "'C" then
temp.Celsius <- Double.Parse(s.Remove(s.LastIndexOf '\'', 2), styles, provider)
else
temp.Value <- Double.Parse(s, styles, provider)
temp
interface IComparable with
// IComparable.CompareTo implementation.
member this.CompareTo(obj: obj) =
match obj with
| null -> 1
| :? Temperature as temp ->
this.Value.CompareTo temp.Value
| _ ->
invalidArg "obj" "object is not a Temperature"
interface IFormattable with
// IFormattable.ToString implementation.
member this.ToString(format: string, provider: IFormatProvider) =
match format with
| "F" ->
$"{this.Value}'F"
| "C" ->
$"{this.Celsius}'C"
| _ ->
this.Value.ToString(format, provider)
' Temperature class stores the value as Double
' and delegates most of the functionality
' to the Double implementation.
Public Class Temperature
Implements IComparable, IFormattable
Public Overloads Function CompareTo(ByVal obj As Object) As Integer _
Implements IComparable.CompareTo
If TypeOf obj Is Temperature Then
Dim temp As Temperature = CType(obj, Temperature)
Return m_value.CompareTo(temp.m_value)
End If
Throw New ArgumentException("object is not a Temperature")
End Function
Public Overloads Function ToString(ByVal format As String, ByVal provider As IFormatProvider) As String _
Implements IFormattable.ToString
If Not (format Is Nothing) Then
If format.Equals("F") Then
Return [String].Format("{0}'F", Me.Value.ToString())
End If
If format.Equals("C") Then
Return [String].Format("{0}'C", Me.Celsius.ToString())
End If
End If
Return m_value.ToString(format, provider)
End Function
' Parses the temperature from a string in form
' [ws][sign]digits['F|'C][ws]
Public Shared Function Parse(ByVal s As String, ByVal styles As NumberStyles, ByVal provider As IFormatProvider) As Temperature
Dim temp As New Temperature()
If s.TrimEnd().EndsWith("'F") Then
temp.Value = Double.Parse(s.Remove(s.LastIndexOf("'"c), 2), styles, provider)
Else
If s.TrimEnd().EndsWith("'C") Then
temp.Celsius = Double.Parse(s.Remove(s.LastIndexOf("'"c), 2), styles, provider)
Else
temp.Value = Double.Parse(s, styles, provider)
End If
End If
Return temp
End Function
' The value holder
Protected m_value As Double
Public Property Value() As Double
Get
Return m_value
End Get
Set(ByVal Value As Double)
m_value = Value
End Set
End Property
Public Property Celsius() As Double
Get
Return (m_value - 32) / 1.8
End Get
Set(ByVal Value As Double)
m_value = Value * 1.8 + 32
End Set
End Property
End Class