System.Single – struktura
Tento článek obsahuje doplňující poznámky k referenční dokumentaci pro toto rozhraní API.
Typ Single hodnoty představuje jednopřesné 32bitové číslo s hodnotami v rozsahu od záporných 3,402823e38 po kladné 3,402823e38, stejně jako kladné nebo záporné nuly, PositiveInfinity, NegativeInfinitya ne číslo (NaN). Má představovat hodnoty, které jsou extrémně velké (například vzdálenosti mezi planetami nebo galaxiemi) nebo extrémně malé (například molekulární hmotnost látky v kg) a které jsou často nepřesné (například vzdálenost od země k jinému slunečnímu systému). Typ Single odpovídá standardu IEC 60559:1989 (IEEE 754) pro binární aritmetické aritmetické hodnoty s plovoucí desetinou čárkou.
System.Single poskytuje metody pro porovnání instancí tohoto typu, pro převod hodnoty instance na řetězcové vyjádření a pro převod řetězcové reprezentace čísla na instanci tohoto typu. Informace o tom, jak kódy specifikace formátu řídí řetězcové znázornění typů hodnot, naleznete v tématu Formátovací typy, Standardní řetězce číselného formátu a Vlastní řetězce číselného formátu.
Znázornění s pohyblivou desetinnou čárkou a přesnost
Datový Single typ ukládá hodnoty s plovoucí desetinnou čárkou s jednoduchou přesností v 32bitovém binárním formátu, jak je znázorněno v následující tabulce:
Část | Bity |
---|---|
Significand or mantissa | 0-22 |
Mocnina | 23-30 |
Znaménko (0 = kladné, 1 = záporné) | 31 |
Stejně jako desetinné zlomky nemohou přesně reprezentovat některé desetinné hodnoty (například 1/3 nebo Math.PI), binární zlomky nemohou představovat některé desetinné hodnoty. Například 2/10, který je reprezentován přesně 0,2 jako desetinná desetinná desetinná čárka, je reprezentován .0011111001001100 jako binární zlomek, se vzorem "1100" se opakuje na nekonečno. V tomto případě poskytuje hodnota s plovoucí desetinou čárkou nepřesnou reprezentaci čísla, které představuje. Provádění dalších matematických operací s původní hodnotou s plovoucí desetinnou čárkou často zvyšuje nedostatek přesnosti. Pokud například porovnáte výsledky násobení 0,3 číslem 10 a přičtením 0,3 až 0,3 devětkrát, uvidíte, že sčítání produkuje méně přesný výsledek, protože zahrnuje osm více operací než násobení. Všimněte si, že tento rozdíl je zjevný pouze v případě, že tyto dvě Single hodnoty zobrazíte pomocí standardního řetězce číselného formátu "R", který v případě potřeby zobrazí všech 9 číslic přesnosti podporovaného typemSingle.
using System;
public class Example12
{
public static void Main()
{
Single value = .2f;
Single result1 = value * 10f;
Single result2 = 0f;
for (int ctr = 1; ctr <= 10; ctr++)
result2 += value;
Console.WriteLine(".2 * 10: {0:R}", result1);
Console.WriteLine(".2 Added 10 times: {0:R}", result2);
}
}
// The example displays the following output:
// .2 * 10: 2
// .2 Added 10 times: 2.00000024
let value = 0.2f
let result1 = value * 10f
let mutable result2 = 0f
for _ = 1 to 10 do
result2 <- result2 + value
printfn $".2 * 10: {result1:R}"
printfn $".2 Added 10 times: {result2:R}"
// The example displays the following output:
// .2 * 10: 2
// .2 Added 10 times: 2.00000024
Module Example13
Public Sub Main()
Dim value As Single = 0.2
Dim result1 As Single = value * 10
Dim result2 As Single
For ctr As Integer = 1 To 10
result2 += value
Next
Console.WriteLine(".2 * 10: {0:R}", result1)
Console.WriteLine(".2 Added 10 times: {0:R}", result2)
End Sub
End Module
' The example displays the following output:
' .2 * 10: 2
' .2 Added 10 times: 2.00000024
Protože některá čísla nemohou být reprezentována přesně jako desetinné binární hodnoty, mohou čísla s plovoucí desetinnou čárkou pouze přibližná reálná čísla.
Všechna čísla s plovoucí desetinou čárkou mají omezený počet významných číslic, což také určuje, jak přesně se hodnota s plovoucí desetinou čárkou blíží skutečnému číslu. Hodnota Single má až 7 desetinných míst přesnosti, i když se interně udržuje maximálně 9 číslic. To znamená, že některé operace s plovoucí desetinnou čárkou nemusí mít přesnost, aby se změnila hodnota s plovoucí desetinnou čárkou. Následující příklad definuje velkou hodnotu s plovoucí desetinnou čárkou s jednou přesností a potom k ní přidá součin Single.Epsilon a jednu čtyřúhelníkovou hodnotu. Produkt je však příliš malý, aby změnil původní hodnotu s plovoucí desetinou čárkou. Nejméně významná číslice je tisíce, zatímco nejvýznamnější číslice v produktu je 10–30.
using System;
public class Example13
{
public static void Main()
{
Single value = 123.456f;
Single additional = Single.Epsilon * 1e15f;
Console.WriteLine($"{value} + {additional} = {value + additional}");
}
}
// The example displays the following output:
// 123.456 + 1.401298E-30 = 123.456
open System
let value = 123.456f
let additional = Single.Epsilon * 1e15f
printfn $"{value} + {additional} = {value + additional}"
// The example displays the following output:
// 123.456 + 1.401298E-30 = 123.456
Module Example
Public Sub Main()
Dim value As Single = 123.456
Dim additional As Single = Single.Epsilon * 1e15
Console.WriteLine($"{value} + {additional} = {value + additional}")
End Sub
End Module
' The example displays the following output:
' 123.456 + 1.401298E-30 = 123.456
Omezená přesnost čísla s plovoucí desetinnou čárkou má několik důsledků:
Dvě čísla s plovoucí desetinnou čárkou, která se zobrazují stejně pro určitou přesnost, nemusí porovnat stejnou hodnotu, protože jejich nejméně významné číslice se liší. V následujícím příkladu se sečtou řady čísel a jejich celkový součet se porovná s očekávaným součtem. I když se tyto dvě hodnoty jeví jako stejné, volání
Equals
metody indikuje, že nejsou.using System; public class Example9 { public static void Main() { Single[] values = { 10.01f, 2.88f, 2.88f, 2.88f, 9.0f }; Single result = 27.65f; Single total = 0f; 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 ({0}) does not equal the total ({1}).", total, result); } } // The example displays the following output: // The sum of the values (27.65) does not equal the total (27.65). // // 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.6500015) does not equal the total (27.65).
let values = [| 10.01f; 2.88f; 2.88f; 2.88f; 9f |] let result = 27.65f let mutable total = 0f for value in values do total <- total + value 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 (27.65) does not equal the total (27.65). // // 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.6500015) does not equal the total (27.65).
Module Example10 Public Sub Main() Dim values() As Single = {10.01, 2.88, 2.88, 2.88, 9.0} Dim result As Single = 27.65 Dim total As Single 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 (27.65) does not equal the total (27.65). ' ' 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).
Pokud změníte položky formátu v Console.WriteLine(String, Object, Object) příkazu z
{0}
a{1}
na{0:R}
a{1:R}
pro zobrazení všech významných číslic dvou Single hodnot, je jasné, že tyto dvě hodnoty jsou nerovné kvůli ztrátě přesnosti během operací sčítání. V tomto případě lze problém vyřešit voláním Math.Round(Double, Int32) metody, která před porovnáním zaokrouhlí Single hodnoty na požadovanou přesnost.Matematická nebo porovnávací operace, která používá číslo s plovoucí desetinnou čárkou, nemusí přinést stejný výsledek, pokud se použije desetinné číslo, protože binární číslo s plovoucí desetinnou čárkou se nemusí shodovat s desetinným číslem. Předchozí příklad znázorňuje výsledek vynásobení 0,3 číslem 10 a přičtením 0,3 do 0,3 devětkrát.
Pokud je přesnost v numerických operacích s desetinnými hodnotami důležitá, použijte Decimal místo typu typ Single . Pokud je přesnost v numerických operacích s celočíselnými hodnotami nad rámec rozsahu Int64 typů UInt64 důležitá, použijte typ BigInteger .
Hodnota nemusí být zaokrouhlována, pokud je zahrnuté číslo s plovoucí desetinou čárkou. Hodnota se říká, že zaokrouhlit, pokud operace převede původní číslo s plovoucí desetinou čárkou na jiný formulář, inverzní operace transformuje převedený formulář zpět na číslo s plovoucí desetinou čárkou a konečné číslo s plovoucí desetinou čárkou se rovná původnímu číslu s plovoucí desetinou čárkou. Odezva může selhat, protože při převodu dojde ke ztrátě nebo změně nejméně významných číslic. V následujícím příkladu jsou tři Single hodnoty převedeny na řetězce a uloženy v souboru. Jak ukazuje výstup, i když se zdá, že hodnoty jsou identické, obnovené hodnoty se nerovnají původním hodnotám.
using System; using System.IO; public class Example10 { public static void Main() { StreamWriter sw = new StreamWriter(@".\Singles.dat"); Single[] values = { 3.2f / 1.11f, 1.0f / 3f, (float)Math.PI }; for (int ctr = 0; ctr < values.Length; ctr++) { sw.Write(values[ctr].ToString()); if (ctr != values.Length - 1) sw.Write("|"); } sw.Close(); Single[] restoredValues = new Single[values.Length]; StreamReader sr = new StreamReader(@".\Singles.dat"); string temp = sr.ReadToEnd(); string[] tempStrings = temp.Split('|'); for (int ctr = 0; ctr < tempStrings.Length; ctr++) restoredValues[ctr] = Single.Parse(tempStrings[ctr]); for (int ctr = 0; ctr < values.Length; ctr++) Console.WriteLine("{0} {2} {1}", values[ctr], restoredValues[ctr], values[ctr].Equals(restoredValues[ctr]) ? "=" : "<>"); } } // The example displays the following output: // 2.882883 <> 2.882883 // 0.3333333 <> 0.3333333 // 3.141593 <> 3.141593
open System open System.IO let values = [| 3.2f / 1.11f; 1f / 3f; MathF.PI |] do use sw = new StreamWriter(@".\Singles.dat") for i = 0 to values.Length - 1 do sw.Write(string values[i]) if i <> values.Length - 1 then sw.Write "|" let restoredValues = use sr = new StreamReader(@".\Singles.dat") sr.ReadToEnd().Split '|' |> Array.map Single.Parse 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.882883 <> 2.882883 // 0.3333333 <> 0.3333333 // 3.141593 <> 3.141593
Imports System.IO Module Example11 Public Sub Main() Dim sw As New StreamWriter(".\Singles.dat") Dim values() As Single = {3.2 / 1.11, 1.0 / 3, CSng(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 Single Dim sr As New StreamReader(".\Singles.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) = Single.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.882883 <> 2.882883 ' 0.3333333 <> 0.3333333 ' 3.141593 <> 3.141593
V tomto případě lze hodnoty úspěšně zaokrouhlit pomocí standardního řetězce číselného Single formátu "G9", aby se zachovala úplná přesnost hodnot, jak ukazuje následující příklad.
using System; using System.IO; public class Example11 { public static void Main() { StreamWriter sw = new StreamWriter(@".\Singles.dat"); Single[] values = { 3.2f / 1.11f, 1.0f / 3f, (float)Math.PI }; for (int ctr = 0; ctr < values.Length; ctr++) sw.Write("{0:G9}{1}", values[ctr], ctr < values.Length - 1 ? "|" : ""); sw.Close(); Single[] restoredValues = new Single[values.Length]; StreamReader sr = new StreamReader(@".\Singles.dat"); string temp = sr.ReadToEnd(); string[] tempStrings = temp.Split('|'); for (int ctr = 0; ctr < tempStrings.Length; ctr++) restoredValues[ctr] = Single.Parse(tempStrings[ctr]); for (int ctr = 0; ctr < values.Length; ctr++) Console.WriteLine("{0} {2} {1}", values[ctr], restoredValues[ctr], values[ctr].Equals(restoredValues[ctr]) ? "=" : "<>"); } } // The example displays the following output: // 2.882883 = 2.882883 // 0.3333333 = 0.3333333 // 3.141593 = 3.141593
open System open System.IO let values = [| 3.2f / 1.11f; 1f / 3f; MathF.PI |] do use sw = new StreamWriter(@".\Singles.dat") for i = 0 to values.Length - 1 do sw.Write $"""{values[i]:G9}{if i < values.Length - 1 then "|" else ""}""" let restoredValues = use sr = new StreamReader(@".\Singles.dat") sr.ReadToEnd().Split '|' |> Array.map Single.Parse 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.882883 = 2.882883 // 0.3333333 = 0.3333333 // 3.141593 = 3.141593
Imports System.IO Module Example12 Public Sub Main() Dim sw As New StreamWriter(".\Singles.dat") Dim values() As Single = {3.2 / 1.11, 1.0 / 3, CSng(Math.PI)} For ctr As Integer = 0 To values.Length - 1 sw.Write("{0:G9}{1}", values(ctr), If(ctr < values.Length - 1, "|", "")) Next sw.Close() Dim restoredValues(values.Length - 1) As Single Dim sr As New StreamReader(".\Singles.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) = Single.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.882883 = 2.882883 ' 0.3333333 = 0.3333333 ' 3.141593 = 3.141593
Single hodnoty mají menší přesnost než Double hodnoty. Hodnota Single , která je převedena na zdánlivě ekvivalentní Double často nerovná Double hodnotu kvůli rozdílům v přesnosti. V následujícím příkladu je výsledek identických operací dělení přiřazen k hodnotě Double a hodnotě Single . Single Po přetypování hodnoty na hodnotu Double, porovnání dvou hodnot ukazuje, že jsou nerovné.
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("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2)); } } // The example displays the following output: // 0.33333333333333331 = 0.3333333432674408: False
open 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: False
Module 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: False
Chcete-li se tomuto problému vyhnout, použijte Double datový typ místo datového Single typu nebo použijte metodu Round , aby obě hodnoty měly stejnou přesnost.
Testování rovnosti
Aby byly považovány za stejné, musí dvě Single hodnoty představovat identické hodnoty. Vzhledem k rozdílům v přesnosti mezi hodnotami nebo kvůli ztrátě přesnosti o jednu nebo obě hodnoty však hodnoty s plovoucí desetinnou čárkou, u které se očekává, že budou identické, se často zdají být nerovné kvůli rozdílům v nejméně významných číslicích. Výsledkem je, že volání Equals metody k určení, zda jsou dvě hodnoty stejné, nebo volání CompareTo metody určit vztah mezi dvěma Single hodnotami, často přinést neočekávané výsledky. To je zřejmé v následujícím příkladu, kdy se dvě zdánlivě stejné Single hodnoty ukázaly jako nerovné, protože první hodnota má 7 číslic přesnosti, zatímco druhá hodnota má 9.
using System;
public class Example
{
public static void Main()
{
float value1 = .3333333f;
float value2 = 1.0f/3;
Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2));
}
}
// The example displays the following output:
// 0.3333333 = 0.333333343: False
let value1 = 0.3333333f
let value2 = 1f / 3f
printfn $"{value1:R} = {value2:R}: {value1.Equals value2}"
// The example displays the following output:
// 0.3333333 = 0.333333343: False
Module Example1
Public Sub Main()
Dim value1 As Single = 0.3333333
Dim value2 As Single = 1 / 3
Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2))
End Sub
End Module
' The example displays the following output:
' 0.3333333 = 0.333333343: False
Počítané hodnoty, které se řídí různými cestami kódu a které jsou manipulovány různými způsoby, se často ukazují jako nerovné. V následujícím příkladu je jedna Single hodnota kvadradrace a poté se vypočítá druhá odmocnina pro obnovení původní hodnoty. Sekunda Single se vynásobí hodnotou 3,51 a druhou druhou odmocninou výsledku se vydělí hodnotou 3,51, aby se obnovila původní hodnota. I když se tyto dvě hodnoty jeví jako identické, volání Equals(Single) metody indikuje, že se nerovnají. Pomocí standardního formátového řetězce "G9" vrátíte výsledný řetězec, který zobrazí všechny významné číslice každé Single hodnoty, že druhá hodnota je .0000000000001 menší než první.
using System;
public class Example1
{
public static void Main()
{
float value1 = 10.201438f;
value1 = (float)Math.Sqrt((float)Math.Pow(value1, 2));
float value2 = (float)Math.Pow((float)value1 * 3.51f, 2);
value2 = ((float)Math.Sqrt(value2)) / 3.51f;
Console.WriteLine("{0} = {1}: {2}\n",
value1, value2, value1.Equals(value2));
Console.WriteLine("{0:G9} = {1:G9}", value1, value2);
}
}
// The example displays the following output:
// 10.20144 = 10.20144: False
//
// 10.201438 = 10.2014389
let value1 =
10.201438f ** 2f
|> sqrt
let value2 =
((value1 * 3.51f) ** 2f |> sqrt) / 3.51f
printfn $"{value1} = {value2}: {value1.Equals value2}\n"
printfn $"{value1:G9} = {value2:G9}"
// The example displays the following output:
// 10.20144 = 10.20144: False
//
// 10.201438 = 10.2014389
Module Example2
Public Sub Main()
Dim value1 As Single = 10.201438
value1 = CSng(Math.Sqrt(CSng(Math.Pow(value1, 2))))
Dim value2 As Single = CSng(Math.Pow(value1 * CSng(3.51), 2))
value2 = CSng(Math.Sqrt(value2) / CSng(3.51))
Console.WriteLine("{0} = {1}: {2}",
value1, value2, value1.Equals(value2))
Console.WriteLine()
Console.WriteLine("{0:G9} = {1:G9}", value1, value2)
End Sub
End Module
' The example displays the following output:
' 10.20144 = 10.20144: False
'
' 10.201438 = 10.2014389
V případech, kdy ztráta přesnosti pravděpodobně ovlivní výsledek porovnání, můžete místo volání Equals nebo CompareTo metody použít následující techniky:
Zavolejte metodu Math.Round , aby se zajistilo, že obě hodnoty mají stejnou přesnost. Následující příklad upraví předchozí příklad tak, aby tento přístup používal tak, aby dvě desetinné hodnoty byly ekvivalentní.
using System; public class Example2 { public static void Main() { float value1 = .3333333f; float value2 = 1.0f / 3; int precision = 7; value1 = (float)Math.Round(value1, precision); value2 = (float)Math.Round(value2, precision); Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2)); } } // The example displays the following output: // 0.3333333 = 0.3333333: True
open System let value1 = 0.3333333f let value2 = 1f / 3f let precision = 7 let value1r = Math.Round(float value1, precision) |> float32 let value2r = Math.Round(float value2, precision) |> float32 printfn $"{value1:R} = {value2:R}: {value1.Equals value2}" // The example displays the following output: // 0.3333333 = 0.3333333: True
Module Example3 Public Sub Main() Dim value1 As Single = 0.3333333 Dim value2 As Single = 1 / 3 Dim precision As Integer = 7 value1 = CSng(Math.Round(value1, precision)) value2 = CSng(Math.Round(value2, precision)) Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2)) End Sub End Module ' The example displays the following output: ' 0.3333333 = 0.3333333: True
Problém přesnosti se stále týká zaokrouhlování středních hodnot. Další informace najdete v Math.Round(Double, Int32, MidpointRounding) metodě.
Otestujte přibližnou rovnost místo rovnosti. Tato technika vyžaduje, abyste definovali buď absolutní částku, o kterou se tyto dvě hodnoty mohou lišit, ale stále se rovnají, nebo definujete relativní množství, o které se menší hodnota může od větší hodnoty lišit.
Upozorňující
Single.Epsilon při testování rovnosti se někdy používá jako absolutní míra vzdálenosti mezi dvěma Single hodnotami. Single.Epsilon Měří však nejmenší možnou hodnotu, ke které lze přidat nebo odečíst, jejíž Single hodnota je nula. U většiny kladných a záporných SingleSingle.Epsilon hodnot je hodnota příliš malá, aby se zjistila. Proto s výjimkou hodnot, které jsou nula, nedoporučujeme jeho použití v testech rovnosti.
Následující příklad používá druhý přístup k definování
IsApproximatelyEqual
metody, která testuje relativní rozdíl mezi dvěma hodnotami. Také kontrastuje výsledek volání metodyIsApproximatelyEqual
a Equals(Single) metody.using System; public class Example3 { public static void Main() { float one1 = .1f * 10; float one2 = 0f; for (int ctr = 1; ctr <= 10; ctr++) one2 += .1f; Console.WriteLine("{0:R} = {1:R}: {2}", one1, one2, one1.Equals(one2)); Console.WriteLine("{0:R} is approximately equal to {1:R}: {2}", one1, one2, IsApproximatelyEqual(one1, one2, .000001f)); } static bool IsApproximatelyEqual(float value1, float value2, float 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 = 1.00000012: False // 1 is approximately equal to 1.00000012: True
open System let isApproximatelyEqual value1 value2 epsilon = // If they are equal anyway, just return True. if value1.Equals value2 then true // Handle NaN, Infinity. elif Single.IsInfinity value1 || Single.IsNaN value1 then value1.Equals value2 elif Single.IsInfinity value2 || Single.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.1f * 10f let mutable one2 = 0f for _ = 1 to 10 do one2 <- one2 + 0.1f printfn $"{one1:R} = {one2:R}: {one1.Equals one2}" printfn $"{one1:R} is approximately equal to {one2:R}: {isApproximatelyEqual one1 one2 0.000001f}" // The example displays the following output: // 1 = 1.00000012: False // 1 is approximately equal to 1.00000012: True
Module Example4 Public Sub Main() Dim one1 As Single = 0.1 * 10 Dim one2 As Single = 0 For ctr As Integer = 1 To 10 one2 += CSng(0.1) Next Console.WriteLine("{0:R} = {1:R}: {2}", one1, one2, one1.Equals(one2)) Console.WriteLine("{0:R} is approximately equal to {1:R}: {2}", one1, one2, IsApproximatelyEqual(one1, one2, 0.000001)) End Sub Function IsApproximatelyEqual(value1 As Single, value2 As Single, epsilon As Single) As Boolean ' If they are equal anyway, just return True. If value1.Equals(value2) Then Return True ' Handle NaN, Infinity. If Single.IsInfinity(value1) Or Single.IsNaN(value1) Then Return value1.Equals(value2) ElseIf Single.IsInfinity(value2) Or Single.IsNaN(value2) Then Return value1.Equals(value2) End If ' Handle zero to avoid division by zero Dim divisor As Single = 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 = 1.00000012: False ' 1 is approximately equal to 1.00000012: True
Hodnoty s plovoucí desetinnou čárkou a výjimky
Operace s hodnotami s plovoucí desetinou čárkou nevyvolávají výjimky, na rozdíl od operací s celočíselnými typy, které v případě neplatných operací, jako je dělení nulou nebo přetečením, vyvolává výjimky. V těchto situacích je výsledkem operace s plovoucí desetinnou čárkou nula, kladné nekonečno, záporné nekonečno nebo ne číslo (NaN):
Pokud je výsledek operace s plovoucí desetinou čárkou příliš malý pro cílový formát, je výsledek nula. K tomu může dojít, když se vynásobí dvě velmi malá čísla s plovoucí desetinnou čárkou, jak ukazuje následující příklad.
using System; public class Example6 { public static void Main() { float value1 = 1.163287e-36f; float value2 = 9.164234e-25f; float result = value1 * value2; Console.WriteLine("{0} * {1} = {2}", value1, value2, result); Console.WriteLine("{0} = 0: {1}", result, result.Equals(0.0f)); } } // The example displays the following output: // 1.163287E-36 * 9.164234E-25 = 0 // 0 = 0: True
let value1 = 1.163287e-36f let value2 = 9.164234e-25f let result = value1 * value2 printfn $"{value1} * {value2} = {result}" printfn $"{result} = 0: {result.Equals(0f)}" // The example displays the following output: // 1.163287E-36 * 9.164234E-25 = 0 // 0 = 0: True
Module Example7 Public Sub Main() Dim value1 As Single = 1.163287E-36 Dim value2 As Single = 9.164234E-25 Dim result As Single = value1 * value2 Console.WriteLine("{0} * {1} = {2:R}", value1, value2, result) Console.WriteLine("{0} = 0: {1}", result, result.Equals(0)) End Sub End Module ' The example displays the following output: ' 1.163287E-36 * 9.164234E-25 = 0 ' 0 = 0: True
Pokud velikost výsledku operace s plovoucí desetinou čárkou překročí rozsah cílového formátu, výsledek operace je PositiveInfinity nebo NegativeInfinity, podle znaménka výsledku. Výsledek operace, která přeteče Single.MaxValue , je PositiveInfinitya výsledek operace, která přeteče Single.MinValue je NegativeInfinity, jak ukazuje následující příklad.
using System; public class Example7 { public static void Main() { float value1 = 3.065e35f; float value2 = 6.9375e32f; float result = value1 * value2; Console.WriteLine("PositiveInfinity: {0}", Single.IsPositiveInfinity(result)); Console.WriteLine("NegativeInfinity: {0}\n", Single.IsNegativeInfinity(result)); value1 = -value1; result = value1 * value2; Console.WriteLine("PositiveInfinity: {0}", Single.IsPositiveInfinity(result)); Console.WriteLine("NegativeInfinity: {0}", Single.IsNegativeInfinity(result)); } } // The example displays the following output: // PositiveInfinity: True // NegativeInfinity: False // // PositiveInfinity: False // NegativeInfinity: True
open System let value1 = 3.065e35f let value2 = 6.9375e32f let result = value1 * value2 printfn $"PositiveInfinity: {Single.IsPositiveInfinity result}" printfn $"NegativeInfinity: {Single.IsNegativeInfinity result}\n" let value3 = -value1 let result2 = value3 * value2 printfn $"PositiveInfinity: {Single.IsPositiveInfinity result}" printfn $"NegativeInfinity: {Single.IsNegativeInfinity result}" // The example displays the following output: // PositiveInfinity: True // NegativeInfinity: False // // PositiveInfinity: False // NegativeInfinity: True
Module Example8 Public Sub Main() Dim value1 As Single = 3.065E+35 Dim value2 As Single = 6.9375E+32 Dim result As Single = value1 * value2 Console.WriteLine("PositiveInfinity: {0}", Single.IsPositiveInfinity(result)) Console.WriteLine("NegativeInfinity: {0}", Single.IsNegativeInfinity(result)) Console.WriteLine() value1 = -value1 result = value1 * value2 Console.WriteLine("PositiveInfinity: {0}", Single.IsPositiveInfinity(result)) Console.WriteLine("NegativeInfinity: {0}", Single.IsNegativeInfinity(result)) End Sub End Module ' The example displays the following output: ' PositiveInfinity: True ' NegativeInfinity: False ' ' PositiveInfinity: False ' NegativeInfinity: True
PositiveInfinity výsledkem je také dělení nulou s kladným dividendem a NegativeInfinity výsledkem dělení nulou se zápornou dividendou.
Pokud je operace s plovoucí deseti čárkou neplatná, výsledkem operace je NaN. Například NaN výsledky z následujících operací:
- Dělení nulou s dělenou nulou. Všimněte si, že jiné případy dělení nulou mají za následek buď PositiveInfinity nebo NegativeInfinity.
- Jakákoli operace s plovoucí desetinou čárkou s neplatným vstupem. Například pokus o nalezení druhou odmocninu záporné hodnoty vrátí NaN.
- Jakákoli operace s argumentem, jehož hodnota je Single.NaN.
Převody typů
Struktura Single nedefinuje žádné explicitní ani implicitní operátory převodu. Místo toho kompilátor implementuje převody.
Následující tabulka uvádí možné převody hodnoty ostatních primitivních číselných typů na Single hodnotu. Označuje také, zda je převod rozšiřující nebo zúžený a zda výsledek Single může mít menší přesnost než původní hodnota.
Převod z | Rozšíření/zužování | Možná ztráta přesnosti |
---|---|---|
Byte | Rozšíření | No |
Decimal | Rozšíření Všimněte si, že jazyk C# vyžaduje operátor přetypování. |
Ano. Decimal podporuje 29 desetinných míst přesnosti; Single podporuje 9. |
Double | Zúžení; Hodnoty mimo rozsah jsou převedeny na Double.NegativeInfinity nebo Double.PositiveInfinity. | Ano. Double podporuje 17 desetinných míst přesnosti; Single podporuje 9. |
Int16 | Rozšíření | No |
Int32 | Rozšíření | Ano. Int32 podporuje 10 desetinných míst přesnosti; Single podporuje 9. |
Int64 | Rozšíření | Ano. Int64 podporuje 19 desetinných míst přesnosti; Single podporuje 9. |
SByte | Rozšíření | No |
UInt16 | Rozšíření | No |
UInt32 | Rozšíření | Ano. UInt32 podporuje 10 desetinných míst přesnosti; Single podporuje 9. |
UInt64 | Rozšíření | Ano. Int64 podporuje 20 desetinných míst přesnosti; Single podporuje 9. |
Následující příklad převede minimální nebo maximální hodnotu jiných primitivních číselných typů na Single hodnotu.
using System;
public class Example4
{
public static void Main()
{
dynamic[] values = { Byte.MinValue, Byte.MaxValue, Decimal.MinValue,
Decimal.MaxValue, Double.MinValue, Double.MaxValue,
Int16.MinValue, Int16.MaxValue, Int32.MinValue,
Int32.MaxValue, Int64.MinValue, Int64.MaxValue,
SByte.MinValue, SByte.MaxValue, UInt16.MinValue,
UInt16.MaxValue, UInt32.MinValue, UInt32.MaxValue,
UInt64.MinValue, UInt64.MaxValue };
float sngValue;
foreach (var value in values)
{
if (value.GetType() == typeof(Decimal) ||
value.GetType() == typeof(Double))
sngValue = (float)value;
else
sngValue = value;
Console.WriteLine("{0} ({1}) --> {2:R} ({3})",
value, value.GetType().Name,
sngValue, sngValue.GetType().Name);
}
}
}
// The example displays the following output:
// 0 (Byte) --> 0 (Single)
// 255 (Byte) --> 255 (Single)
// -79228162514264337593543950335 (Decimal) --> -7.92281625E+28 (Single)
// 79228162514264337593543950335 (Decimal) --> 7.92281625E+28 (Single)
// -1.79769313486232E+308 (Double) --> -Infinity (Single)
// 1.79769313486232E+308 (Double) --> Infinity (Single)
// -32768 (Int16) --> -32768 (Single)
// 32767 (Int16) --> 32767 (Single)
// -2147483648 (Int32) --> -2.14748365E+09 (Single)
// 2147483647 (Int32) --> 2.14748365E+09 (Single)
// -9223372036854775808 (Int64) --> -9.223372E+18 (Single)
// 9223372036854775807 (Int64) --> 9.223372E+18 (Single)
// -128 (SByte) --> -128 (Single)
// 127 (SByte) --> 127 (Single)
// 0 (UInt16) --> 0 (Single)
// 65535 (UInt16) --> 65535 (Single)
// 0 (UInt32) --> 0 (Single)
// 4294967295 (UInt32) --> 4.2949673E+09 (Single)
// 0 (UInt64) --> 0 (Single)
// 18446744073709551615 (UInt64) --> 1.84467441E+19 (Single)
open System
let values: obj list =
[ Byte.MinValue; Byte.MaxValue; Decimal.MinValue
Decimal.MaxValue; Double.MinValue; Double.MaxValue
Int16.MinValue; Int16.MaxValue; Int32.MinValue
Int32.MaxValue; Int64.MinValue; Int64.MaxValue
SByte.MinValue; SByte.MaxValue; UInt16.MinValue
UInt16.MaxValue; UInt32.MinValue; UInt32.MaxValue
UInt64.MinValue; UInt64.MaxValue ]
for value in values do
let sngValue =
match value with
| :? byte as v -> float32 v
| :? decimal as v -> float32 v
| :? double as v -> float32 v
| :? int16 as v -> float32 v
| :? int as v -> float32 v
| :? int64 as v -> float32 v
| :? int8 as v -> float32 v
| :? uint16 as v -> float32 v
| :? uint as v -> float32 v
| :? uint64 as v -> float32 v
| _ -> raise (NotImplementedException "Unknown Type")
printfn $"{value} ({value.GetType().Name}) --> {sngValue:R} ({sngValue.GetType().Name})"
// The example displays the following output:
// 0 (Byte) --> 0 (Single)
// 255 (Byte) --> 255 (Single)
// -79228162514264337593543950335 (Decimal) --> -7.92281625E+28 (Single)
// 79228162514264337593543950335 (Decimal) --> 7.92281625E+28 (Single)
// -1.79769313486232E+308 (Double) --> -Infinity (Single)
// 1.79769313486232E+308 (Double) --> Infinity (Single)
// -32768 (Int16) --> -32768 (Single)
// 32767 (Int16) --> 32767 (Single)
// -2147483648 (Int32) --> -2.14748365E+09 (Single)
// 2147483647 (Int32) --> 2.14748365E+09 (Single)
// -9223372036854775808 (Int64) --> -9.223372E+18 (Single)
// 9223372036854775807 (Int64) --> 9.223372E+18 (Single)
// -128 (SByte) --> -128 (Single)
// 127 (SByte) --> 127 (Single)
// 0 (UInt16) --> 0 (Single)
// 65535 (UInt16) --> 65535 (Single)
// 0 (UInt32) --> 0 (Single)
// 4294967295 (UInt32) --> 4.2949673E+09 (Single)
// 0 (UInt64) --> 0 (Single)
// 18446744073709551615 (UInt64) --> 1.84467441E+19 (Single)
Module Example5
Public Sub Main()
Dim values() As Object = {Byte.MinValue, Byte.MaxValue, Decimal.MinValue,
Decimal.MaxValue, Double.MinValue, Double.MaxValue,
Int16.MinValue, Int16.MaxValue, Int32.MinValue,
Int32.MaxValue, Int64.MinValue, Int64.MaxValue,
SByte.MinValue, SByte.MaxValue, UInt16.MinValue,
UInt16.MaxValue, UInt32.MinValue, UInt32.MaxValue,
UInt64.MinValue, UInt64.MaxValue}
Dim sngValue As Single
For Each value In values
If value.GetType() = GetType(Double) Then
sngValue = CSng(value)
Else
sngValue = value
End If
Console.WriteLine("{0} ({1}) --> {2:R} ({3})",
value, value.GetType().Name,
sngValue, sngValue.GetType().Name)
Next
End Sub
End Module
' The example displays the following output:
' 0 (Byte) --> 0 (Single)
' 255 (Byte) --> 255 (Single)
' -79228162514264337593543950335 (Decimal) --> -7.92281625E+28 (Single)
' 79228162514264337593543950335 (Decimal) --> 7.92281625E+28 (Single)
' -1.79769313486232E+308 (Double) --> -Infinity (Single)
' 1.79769313486232E+308 (Double) --> Infinity (Single)
' -32768 (Int16) --> -32768 (Single)
' 32767 (Int16) --> 32767 (Single)
' -2147483648 (Int32) --> -2.14748365E+09 (Single)
' 2147483647 (Int32) --> 2.14748365E+09 (Single)
' -9223372036854775808 (Int64) --> -9.223372E+18 (Single)
' 9223372036854775807 (Int64) --> 9.223372E+18 (Single)
' -128 (SByte) --> -128 (Single)
' 127 (SByte) --> 127 (Single)
' 0 (UInt16) --> 0 (Single)
' 65535 (UInt16) --> 65535 (Single)
' 0 (UInt32) --> 0 (Single)
' 4294967295 (UInt32) --> 4.2949673E+09 (Single)
' 0 (UInt64) --> 0 (Single)
' 18446744073709551615 (UInt64) --> 1.84467441E+19 (Single)
Kromě toho hodnoty DoubleDouble.NaN, Double.PositiveInfinitya Double.NegativeInfinity převést na Single.NaN, Single.PositiveInfinitya Single.NegativeInfinity, v uvedeném pořadí.
Všimněte si, že převod hodnoty některých číselných typů na Single hodnotu může zahrnovat ztrátu přesnosti. Jak ukazuje příklad, ztráta přesnosti je možná při převodu Decimal, Double, Int32, , Int64, UInt32a UInt64 hodnoty na Single hodnoty.
Single Převod hodnoty na hodnotu Double je rozšiřující převod. Převod může způsobit ztrátu přesnosti, pokud Double typ nemá přesné vyjádření hodnoty Single .
Převod hodnoty na hodnotu libovolného primitivního číselného datového Single typu jiného než Double je zužující převod a vyžaduje operátor přetypování (v jazyce C#) nebo metodu převodu (v jazyce Visual Basic). Hodnoty mimo rozsah cílového datového typu definovaného cílovým typem MinValue
a MaxValue
vlastnostmi se chovají, jak je znázorněno v následující tabulce.
Typ cíle | Výsledek |
---|---|
Libovolný celočíselný typ | Výjimka OverflowException , pokud dojde k převodu v kontrolovaném kontextu. Pokud k převodu dojde v nezaškrtnutém kontextu (výchozí hodnota v jazyce C#), operace převodu bude úspěšná, ale hodnota se přeteče. |
Decimal | Výjimka OverflowException : |
Kromě toho , Single.NaNSingle.PositiveInfinitya vyvolat Single.NegativeInfinityOverflowException pro převody na celá čísla v zaškrtnutém kontextu, ale tyto hodnoty přetékají při převodu na celá čísla v nezaškrtnutém kontextu. Pro převody na Decimal, vždy vyvolat OverflowException. Pro převody na Double, převedou na Double.NaN, Double.PositiveInfinitya Double.NegativeInfinity, v uvedeném pořadí.
Všimněte si, že ztráta přesnosti může mít za následek převod Single hodnoty na jiný číselný typ. V případě převodu ne integrálních Single hodnot, jak ukazuje výstup z příkladu, se zlomková komponenta ztratí, když Single je hodnota buď zaokrouhlená (jako v jazyce Visual Basic), nebo zkrácená (jako v jazyce C# a F#). U převodů na Decimal hodnoty Single nemusí mít hodnota přesné vyjádření v cílovém datovém typu.
Následující příklad převede počet Single hodnot na několik dalších číselných typů. Převody probíhají v kontrolovaném kontextu v jazyce Visual Basic (výchozí), v jazyce C# (kvůli kontrolovanému klíčovému slovu) a v jazyce F# (z důvodu open Checked
příkazu). Výstup z příkladu ukazuje výsledek pro převody v nezaškrtnutém kontextu. Převody můžete provádět v nezaškrtnutém kontextu v jazyce Visual Basic kompilováním přepínače kompilátoru /removeintchecks+
, v jazyce C# zakomentováním checked
příkazu a v jazyce F# zakomentováním open Checked
příkazu.
using System;
public class Example5
{
public static void Main()
{
float[] values = { Single.MinValue, -67890.1234f, -12345.6789f,
12345.6789f, 67890.1234f, Single.MaxValue,
Single.NaN, Single.PositiveInfinity,
Single.NegativeInfinity };
checked
{
foreach (var value in values)
{
try
{
Int64 lValue = (long)value;
Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
value, value.GetType().Name,
lValue, lValue.GetType().Name);
}
catch (OverflowException)
{
Console.WriteLine("Unable to convert {0} to Int64.", value);
}
try
{
UInt64 ulValue = (ulong)value;
Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
value, value.GetType().Name,
ulValue, ulValue.GetType().Name);
}
catch (OverflowException)
{
Console.WriteLine("Unable to convert {0} to UInt64.", value);
}
try
{
Decimal dValue = (decimal)value;
Console.WriteLine("{0} ({1}) --> {2} ({3})",
value, value.GetType().Name,
dValue, dValue.GetType().Name);
}
catch (OverflowException)
{
Console.WriteLine("Unable to convert {0} to Decimal.", value);
}
Double dblValue = value;
Console.WriteLine("{0} ({1}) --> {2} ({3})",
value, value.GetType().Name,
dblValue, dblValue.GetType().Name);
Console.WriteLine();
}
}
}
}
// The example displays the following output for conversions performed
// in a checked context:
// Unable to convert -3.402823E+38 to Int64.
// Unable to convert -3.402823E+38 to UInt64.
// Unable to convert -3.402823E+38 to Decimal.
// -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
//
// -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
// Unable to convert -67890.13 to UInt64.
// -67890.13 (Single) --> -67890.12 (Decimal)
// -67890.13 (Single) --> -67890.125 (Double)
//
// -12345.68 (Single) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
// Unable to convert -12345.68 to UInt64.
// -12345.68 (Single) --> -12345.68 (Decimal)
// -12345.68 (Single) --> -12345.6787109375 (Double)
//
// 12345.68 (Single) --> 12345 (0x0000000000003039) (Int64)
// 12345.68 (Single) --> 12345 (0x0000000000003039) (UInt64)
// 12345.68 (Single) --> 12345.68 (Decimal)
// 12345.68 (Single) --> 12345.6787109375 (Double)
//
// 67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
// 67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
// 67890.13 (Single) --> 67890.12 (Decimal)
// 67890.13 (Single) --> 67890.125 (Double)
//
// Unable to convert 3.402823E+38 to Int64.
// Unable to convert 3.402823E+38 to UInt64.
// Unable to convert 3.402823E+38 to Decimal.
// 3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
//
// Unable to convert NaN to Int64.
// Unable to convert NaN to UInt64.
// Unable to convert NaN to Decimal.
// NaN (Single) --> NaN (Double)
//
// Unable to convert Infinity to Int64.
// Unable to convert Infinity to UInt64.
// Unable to convert Infinity to Decimal.
// Infinity (Single) --> Infinity (Double)
//
// Unable to convert -Infinity to Int64.
// Unable to convert -Infinity to UInt64.
// Unable to convert -Infinity to Decimal.
// -Infinity (Single) --> -Infinity (Double)
// The example displays the following output for conversions performed
// in an unchecked context:
// -3.402823E+38 (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
// -3.402823E+38 (Single) --> 9223372036854775808 (0x8000000000000000) (UInt64)
// Unable to convert -3.402823E+38 to Decimal.
// -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
//
// -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
// -67890.13 (Single) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE) (UInt64)
// -67890.13 (Single) --> -67890.12 (Decimal)
// -67890.13 (Single) --> -67890.125 (Double)
//
// -12345.68 (Single) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
// -12345.68 (Single) --> 18446744073709539271 (0xFFFFFFFFFFFFCFC7) (UInt64)
// -12345.68 (Single) --> -12345.68 (Decimal)
// -12345.68 (Single) --> -12345.6787109375 (Double)
//
// 12345.68 (Single) --> 12345 (0x0000000000003039) (Int64)
// 12345.68 (Single) --> 12345 (0x0000000000003039) (UInt64)
// 12345.68 (Single) --> 12345.68 (Decimal)
// 12345.68 (Single) --> 12345.6787109375 (Double)
//
// 67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
// 67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
// 67890.13 (Single) --> 67890.12 (Decimal)
// 67890.13 (Single) --> 67890.125 (Double)
//
// 3.402823E+38 (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
// 3.402823E+38 (Single) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert 3.402823E+38 to Decimal.
// 3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
//
// NaN (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
// NaN (Single) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert NaN to Decimal.
// NaN (Single) --> NaN (Double)
//
// Infinity (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
// Infinity (Single) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert Infinity to Decimal.
// Infinity (Single) --> Infinity (Double)
//
// -Infinity (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
// -Infinity (Single) --> 9223372036854775808 (0x8000000000000000) (UInt64)
// Unable to convert -Infinity to Decimal.
// -Infinity (Single) --> -Infinity (Double)
open System
open Checked
let values =
[ Single.MinValue; -67890.1234f; -12345.6789f
12345.6789f; 67890.1234f; Single.MaxValue
Single.NaN; Single.PositiveInfinity
Single.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."
let dblValue = double value
printfn $"{value} ({value.GetType().Name}) --> {dblValue} ({dblValue.GetType().Name})\n"
// The example displays the following output for conversions performed
// in a checked context:
// Unable to convert -3.402823E+38 to Int64.
// Unable to convert -3.402823E+38 to UInt64.
// Unable to convert -3.402823E+38 to Decimal.
// -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
//
// -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
// Unable to convert -67890.13 to UInt64.
// -67890.13 (Single) --> -67890.12 (Decimal)
// -67890.13 (Single) --> -67890.125 (Double)
//
// -12345.68 (Single) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
// Unable to convert -12345.68 to UInt64.
// -12345.68 (Single) --> -12345.68 (Decimal)
// -12345.68 (Single) --> -12345.6787109375 (Double)
//
// 12345.68 (Single) --> 12345 (0x0000000000003039) (Int64)
// 12345.68 (Single) --> 12345 (0x0000000000003039) (UInt64)
// 12345.68 (Single) --> 12345.68 (Decimal)
// 12345.68 (Single) --> 12345.6787109375 (Double)
//
// 67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
// 67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
// 67890.13 (Single) --> 67890.12 (Decimal)
// 67890.13 (Single) --> 67890.125 (Double)
//
// Unable to convert 3.402823E+38 to Int64.
// Unable to convert 3.402823E+38 to UInt64.
// Unable to convert 3.402823E+38 to Decimal.
// 3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
//
// Unable to convert NaN to Int64.
// Unable to convert NaN to UInt64.
// Unable to convert NaN to Decimal.
// NaN (Single) --> NaN (Double)
//
// Unable to convert Infinity to Int64.
// Unable to convert Infinity to UInt64.
// Unable to convert Infinity to Decimal.
// Infinity (Single) --> Infinity (Double)
//
// Unable to convert -Infinity to Int64.
// Unable to convert -Infinity to UInt64.
// Unable to convert -Infinity to Decimal.
// -Infinity (Single) --> -Infinity (Double)
// The example displays the following output for conversions performed
// in an unchecked context:
// -3.402823E+38 (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
// -3.402823E+38 (Single) --> 9223372036854775808 (0x8000000000000000) (UInt64)
// Unable to convert -3.402823E+38 to Decimal.
// -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
//
// -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
// -67890.13 (Single) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE) (UInt64)
// -67890.13 (Single) --> -67890.12 (Decimal)
// -67890.13 (Single) --> -67890.125 (Double)
//
// -12345.68 (Single) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
// -12345.68 (Single) --> 18446744073709539271 (0xFFFFFFFFFFFFCFC7) (UInt64)
// -12345.68 (Single) --> -12345.68 (Decimal)
// -12345.68 (Single) --> -12345.6787109375 (Double)
//
// 12345.68 (Single) --> 12345 (0x0000000000003039) (Int64)
// 12345.68 (Single) --> 12345 (0x0000000000003039) (UInt64)
// 12345.68 (Single) --> 12345.68 (Decimal)
// 12345.68 (Single) --> 12345.6787109375 (Double)
//
// 67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
// 67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
// 67890.13 (Single) --> 67890.12 (Decimal)
// 67890.13 (Single) --> 67890.125 (Double)
//
// 3.402823E+38 (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
// 3.402823E+38 (Single) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert 3.402823E+38 to Decimal.
// 3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
//
// NaN (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
// NaN (Single) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert NaN to Decimal.
// NaN (Single) --> NaN (Double)
//
// Infinity (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
// Infinity (Single) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert Infinity to Decimal.
// Infinity (Single) --> Infinity (Double)
//
// -Infinity (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
// -Infinity (Single) --> 9223372036854775808 (0x8000000000000000) (UInt64)
// Unable to convert -Infinity to Decimal.
// -Infinity (Single) --> -Infinity (Double)
Module Example6
Public Sub Main()
Dim values() As Single = {Single.MinValue, -67890.1234, -12345.6789,
12345.6789, 67890.1234, Single.MaxValue,
Single.NaN, Single.PositiveInfinity,
Single.NegativeInfinity}
For Each value In values
Try
Dim lValue As Long = 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
Dim dblValue As Double = value
Console.WriteLine("{0} ({1}) --> {2} ({3})",
value, value.GetType().Name,
dblValue, dblValue.GetType().Name)
Console.WriteLine()
Next
End Sub
End Module
' The example displays the following output for conversions performed
' in a checked context:
' Unable to convert -3.402823E+38 to Int64.
' Unable to convert -3.402823E+38 to UInt64.
' Unable to convert -3.402823E+38 to Decimal.
' -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
'
' -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
' Unable to convert -67890.13 to UInt64.
' -67890.13 (Single) --> -67890.12 (Decimal)
' -67890.13 (Single) --> -67890.125 (Double)
'
' -12345.68 (Single) --> -12346 (0xFFFFFFFFFFFFCFC6) (Int64)
' Unable to convert -12345.68 to UInt64.
' -12345.68 (Single) --> -12345.68 (Decimal)
' -12345.68 (Single) --> -12345.6787109375 (Double)
'
' 12345.68 (Single) --> 12346 (0x000000000000303A) (Int64)
' 12345.68 (Single) --> 12346 (0x000000000000303A) (UInt64)
' 12345.68 (Single) --> 12345.68 (Decimal)
' 12345.68 (Single) --> 12345.6787109375 (Double)
'
' 67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
' 67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
' 67890.13 (Single) --> 67890.12 (Decimal)
' 67890.13 (Single) --> 67890.125 (Double)
'
' Unable to convert 3.402823E+38 to Int64.
' Unable to convert 3.402823E+38 to UInt64.
' Unable to convert 3.402823E+38 to Decimal.
' 3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
'
' Unable to convert NaN to Int64.
' Unable to convert NaN to UInt64.
' Unable to convert NaN to Decimal.
' NaN (Single) --> NaN (Double)
'
' Unable to convert Infinity to Int64.
' Unable to convert Infinity to UInt64.
' Unable to convert Infinity to Decimal.
' Infinity (Single) --> Infinity (Double)
'
' Unable to convert -Infinity to Int64.
' Unable to convert -Infinity to UInt64.
' Unable to convert -Infinity to Decimal.
' -Infinity (Single) --> -Infinity (Double)
' The example displays the following output for conversions performed
' in an unchecked context:
' -3.402823E+38 (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
' -3.402823E+38 (Single) --> 9223372036854775808 (0x8000000000000000) (UInt64)
' Unable to convert -3.402823E+38 to Decimal.
' -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
'
' -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
' -67890.13 (Single) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE) (UInt64)
' -67890.13 (Single) --> -67890.12 (Decimal)
' -67890.13 (Single) --> -67890.125 (Double)
'
' -12345.68 (Single) --> -12346 (0xFFFFFFFFFFFFCFC6) (Int64)
' -12345.68 (Single) --> 18446744073709539270 (0xFFFFFFFFFFFFCFC6) (UInt64)
' -12345.68 (Single) --> -12345.68 (Decimal)
' -12345.68 (Single) --> -12345.6787109375 (Double)
'
' 12345.68 (Single) --> 12346 (0x000000000000303A) (Int64)
' 12345.68 (Single) --> 12346 (0x000000000000303A) (UInt64)
' 12345.68 (Single) --> 12345.68 (Decimal)
' 12345.68 (Single) --> 12345.6787109375 (Double)
'
' 67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
' 67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
' 67890.13 (Single) --> 67890.12 (Decimal)
' 67890.13 (Single) --> 67890.125 (Double)
'
' 3.402823E+38 (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
' 3.402823E+38 (Single) --> 0 (0x0000000000000000) (UInt64)
' Unable to convert 3.402823E+38 to Decimal.
' 3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
'
' NaN (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
' NaN (Single) --> 0 (0x0000000000000000) (UInt64)
' Unable to convert NaN to Decimal.
' NaN (Single) --> NaN (Double)
'
' Infinity (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
' Infinity (Single) --> 0 (0x0000000000000000) (UInt64)
' Unable to convert Infinity to Decimal.
' Infinity (Single) --> Infinity (Double)
'
' -Infinity (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
' -Infinity (Single) --> 9223372036854775808 (0x8000000000000000) (UInt64)
' Unable to convert -Infinity to Decimal.
' -Infinity (Single) --> -Infinity (Double)
Další informace o převodu číselných typů naleznete v tématu Převod typů v .NET a Převod typů tabulky.
Funkce s plovoucí desetinnou čárkou
Struktura Single a související typy poskytují metody pro provádění následujících kategorií operací:
Porovnání hodnot Můžete volat metodu Equals , abyste zjistili, zda jsou dvě Single hodnoty stejné, nebo CompareTo metoda určující vztah mezi dvěma hodnotami.
Struktura Single také podporuje úplnou sadu relačních operátorů. Můžete například otestovat rovnost nebo nerovnost nebo určit, jestli je jedna hodnota větší nebo rovna jiné hodnotě. Pokud je jedním z operandů Double, Single hodnota se před provedením porovnání převede na Double hodnotu. Pokud je jeden z operandů celočíselným typem, před provedením porovnání se převede na Single typ. I když se jedná o rozšiřující převody, mohou zahrnovat ztrátu přesnosti.
Upozorňující
Vzhledem k rozdílům v přesnosti můžou být dvě Single hodnoty, u kterých očekáváte, že budou stejné, budou nerovné, což má vliv na výsledek porovnání. Další informace o porovnávání dvou Single hodnot najdete v části Testování rovnosti.
Můžete také volat IsNaN, IsInfinity, IsPositiveInfinitya IsNegativeInfinity metody testovat pro tyto speciální hodnoty.
Matematické operace. Běžné aritmetické operace, jako jsou sčítání, odčítání, násobení a dělení, jsou implementovány kompilátory jazyka a instrukcemi CIL (Common Intermediate Language), nikoli metodami Single . Pokud druhý operand v matematické operaci je Double, Single je převeden na před Double provedením operace a výsledek operace je také Double hodnota. Pokud je druhý operand celočíselným typem, je před provedením operace převeden na Single celočíselný a výsledek operace je také Single hodnota.
Jiné matematické operace můžete provádět voláním
static
metod jazykaShared
Visual Basic ve System.Math třídě. Patří mezi ně další metody běžně používané pro aritmetické (například , a ), geometrii (například Math.Cos aMath.Sin) a výpočet (například Math.Log).Math.SqrtMath.SignMath.Abs Ve všech případech Single se hodnota převede na Double.Můžete také manipulovat s jednotlivými bity v hodnotě Single . Metoda BitConverter.GetBytes(Single) vrátí svůj bitový vzor v bajtovém poli. Předáním pole bajtů metodě BitConverter.ToInt32 můžete zachovat také Single bitový vzor hodnoty v 32bitovém celočíselném čísle.
Zaokrouhlování. Zaokrouhlování se často používá jako technika pro snížení dopadu rozdílů mezi hodnotami způsobenými problémy reprezentace s plovoucí desetinnou čárkou a přesností. Hodnotu můžete zaokrouhlit Single voláním Math.Round metody. Všimněte si však, že Single hodnota je převedena na před Double zavolání metody a převod může zahrnovat ztrátu přesnosti.
Formátování. Hodnotu můžete převést na řetězcovou Single reprezentaci voláním ToString metody nebo pomocí funkce složeného formátování. Informace o tom, jak formátovací řetězce řídí řetězcové znázornění hodnot s plovoucí desetinnou čárkou, naleznete v tématech Standardní řetězce číselného formátu a Vlastní řetězce číselného formátu.
Analýza řetězců Řetězcovou reprezentaci hodnoty Single s plovoucí desetinnou čárkou můžete převést na hodnotu voláním nebo TryParse metodouParse. Pokud operace analýzy selže, Parse vyvolá metoda výjimku, zatímco TryParse metoda vrátí
false
.Převod typu. Struktura Single poskytuje explicitní implementaci rozhraní pro IConvertible rozhraní, která podporuje převod mezi všemi dvěma standardními datovými typy .NET. Kompilátory jazyka také podporují implicitní převod hodnot pro všechny ostatní standardní číselné typy s výjimkou převodu Double na Single hodnoty. Převod hodnoty libovolného standardního číselného typu jiného než Double na typ Single je rozšiřující převod a nevyžaduje použití operátoru přetypování nebo metody převodu.
Převod 32bitové a 64bitové celočíselné hodnoty ale může zahrnovat ztrátu přesnosti. Následující tabulka uvádí rozdíly v přesnosti pro 32bitovou, 64bitovou verzi a Double typy:
Typ Maximální přesnost (v desítkových číslicích) Vnitřní přesnost (v desítkových číslicích) Double 15 17 Int32 a UInt32 10 10 Int64 a UInt64 19 19 Single 7 9 Problém přesnosti nejčastěji ovlivňuje Single hodnoty, které jsou převedeny na Double hodnoty. V následujícím příkladu jsou dvě hodnoty vytvořené identickými operacemi dělení nerovné, protože jedna z hodnot je hodnota s plovoucí desetinnou čárkou s jednou přesností, která je převedena na Double.
using System; public class Example8 { public static void Main() { Double value1 = 1 / 3.0; Single sValue2 = 1 / 3.0f; Double value2 = (Double)sValue2; Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2)); } } // The example displays the following output: // 0.33333333333333331 = 0.3333333432674408: False
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: False
Module Example9 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: False