System.Single struct
Este artigo fornece observações complementares à documentação de referência para essa API.
O Single tipo de valor representa um número de 32 bits de precisão única com valores que variam de 3,402823e38 negativo a 3,402823e38 positivo, bem como zero positivo ou negativo, PositiveInfinity, NegativeInfinitye não um número (NaN). Destina-se a representar valores que são extremamente grandes (como distâncias entre planetas ou galáxias) ou extremamente pequenos (como a massa molecular de uma substância em quilogramas) e que muitas vezes são imprecisos (como a distância da Terra a outro sistema solar). O Single tipo está em conformidade com o padrão IEC 60559:1989 (IEEE 754) para aritmética de ponto flutuante binário.
System.Single fornece métodos para comparar instâncias desse tipo, converter o valor de uma instância em sua representação de cadeia de caracteres e converter a representação de cadeia de caracteres de um número em uma instância desse tipo. Para obter informações sobre como os códigos de especificação de formato controlam a representação de cadeia de caracteres de tipos de valor, consulte Tipos de formatação, Cadeias de caracteres de formato numérico padrão e Cadeias de caracteres de formato numérico personalizado.
Representação de ponto flutuante e precisão
O Single tipo de dados armazena valores de ponto flutuante de precisão única em um formato binário de 32 bits, conforme mostrado na tabela a seguir:
Parte | Bits |
---|---|
Significand ou mantissa | 0-22 |
Expoente | 23-30 |
Sinal (0 = positivo, 1 = negativo) | 31 |
Assim como as frações decimais são incapazes de representar com precisão alguns valores fracionários (como 1/3 ou Math.PI), as frações binárias são incapazes de representar alguns valores fracionários. Por exemplo, 2/10, que é representado precisamente por .2 como uma fração decimal, é representado por .0011111001001100 como uma fração binária, com o padrão "1100" repetindo ao infinito. Nesse caso, o valor de ponto flutuante fornece uma representação imprecisa do número que ele representa. Realizar operações matemáticas adicionais no valor de ponto flutuante original geralmente aumenta sua falta de precisão. Por exemplo, se você comparar os resultados de multiplicar .3 por 10 e adicionar .3 a .3 nove vezes, verá que a adição produz o resultado menos preciso, porque envolve oito operações a mais do que a multiplicação. Observe que essa disparidade é aparente somente se você exibir os dois Single valores usando a cadeia de caracteres de formato numérico padrão "R", que, se necessário, exibe todos os 9 dígitos de precisão suportados Single pelo tipo.
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
Como alguns números não podem ser representados exatamente como valores binários fracionários, os números de ponto flutuante só podem se aproximar de números reais.
Todos os números de ponto flutuante têm um número limitado de dígitos significativos, o que também determina com que precisão um valor de ponto flutuante se aproxima de um número real. Um Single valor tem até 7 dígitos decimais de precisão, embora um máximo de 9 dígitos seja mantido internamente. Isso significa que algumas operações de ponto flutuante podem não ter a precisão necessária para alterar um valor de ponto flutuante. O exemplo a seguir define um grande valor de ponto flutuante de precisão única e, em seguida, adiciona o produto de Single.Epsilon e um quatrilhão a ele. No entanto, o produto é muito pequeno para modificar o valor original do ponto flutuante. Seu dígito menos significativo é milésimos, enquanto o dígito mais significativo no produto é 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
A precisão limitada de um número de ponto flutuante tem várias consequências:
Dois números de ponto flutuante que pareçam iguais para uma determinada precisão podem não ser comparados como iguais porque seus dígitos menos significantes são diferentes. No exemplo a seguir, uma série de números é somada e seu total é comparado com o total esperado. Embora os dois valores pareçam ser os mesmos, uma chamada para o
Equals
método indica que eles não são.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).
Se você alterar os itens de formato na Console.WriteLine(String, Object, Object) instrução de e
{1}
para e{1:R}
para{0:R}
exibir todos os dígitos significativos dos dois valores, ficará claro que os dois Single valores são desiguais devido a uma perda de precisão durante as operações de{0}
adição. Nesse caso, o problema pode ser resolvido chamando o Math.Round(Double, Int32) método para arredondar os Single valores para a precisão desejada antes de realizar a comparação.Uma operação matemática ou de comparação que usa um número de ponto flutuante pode não produzir o mesmo resultado se um número decimal for usado, porque o número de ponto flutuante binário pode não ser igual ao número decimal. Um exemplo anterior ilustrou isso exibindo o resultado de multiplicar .3 por 10 e adicionar .3 a .3 nove vezes.
Quando a precisão em operações numéricas com valores fracionários for importante, use o Decimal tipo em vez do Single tipo. Quando a precisão em operações numéricas com valores integrais além do intervalo dos Int64 tipos ou UInt64 for importante, use o BigInteger tipo.
Um valor pode não ser de ida e volta se um número de ponto flutuante estiver envolvido. Um valor é dito para ida e volta se uma operação converte um número de ponto flutuante original em outra forma, uma operação inversa transforma a forma convertida de volta em um número de ponto flutuante e o número de ponto flutuante final é igual ao número de ponto flutuante original. A viagem de ida e volta pode falhar porque um ou mais dígitos menos significativos são perdidos ou alterados em uma conversão. No exemplo a seguir, três Single valores são convertidos em cadeias de caracteres e salvos em um arquivo. Como mostra a saída, embora os valores pareçam ser idênticos, os valores restaurados não são iguais aos valores originais.
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
Nesse caso, os valores podem ser arredondados com êxito usando a cadeia de caracteres de formato numérico padrão "G9" para preservar a precisão total dos Single valores, como mostra o exemplo a seguir.
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 os valores têm menos precisão do que Double os valores. Um Single valor que é convertido em um aparentemente equivalente Double muitas vezes não é igual ao Double valor devido a diferenças na precisão. No exemplo a seguir, o resultado de operações de divisão idênticas é atribuído a um valor e a um DoubleSingle valor. Depois que o Single valor é convertido em um Double, uma comparação dos dois valores mostra que eles são desiguais.
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
Para evitar esse problema, use o tipo de dados no lugar do tipo de Single dados ou use o DoubleRound método para que ambos os valores tenham a mesma precisão.
Teste de igualdade
Para serem considerados iguais, dois Single valores devem representar valores idênticos. No entanto, devido a diferenças de precisão entre valores, ou devido a uma perda de precisão por um ou ambos os valores, os valores de ponto flutuante que se espera que sejam idênticos muitas vezes acabam por ser desiguais devido a diferenças nos seus dígitos menos significativos. Como resultado, chamadas para o método para determinar se dois valores são iguais, ou chamadas para o EqualsCompareTo método para determinar a relação entre dois Single valores, geralmente produzem resultados inesperados. Isso fica evidente no exemplo a seguir, onde dois valores aparentemente iguais Single acabam sendo desiguais, pois o primeiro valor tem 7 dígitos de precisão, enquanto o segundo valor tem 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
Valores calculados que seguem caminhos de código diferentes e que são manipulados de maneiras diferentes geralmente se mostram desiguais. No exemplo a seguir, um Single valor é quadrado e, em seguida, a raiz quadrada é calculada para restaurar o valor original. Um segundo Single é multiplicado por 3,51 e quadrado antes que a raiz quadrada do resultado seja dividida por 3,51 para restaurar o valor original. Embora os dois valores pareçam ser idênticos, uma chamada para o Equals(Single) método indica que eles não são iguais. Usar a cadeia de caracteres de formato padrão "G9" para retornar uma cadeia de caracteres de resultado que exibe todos os dígitos significativos de cada Single valor mostra que o segundo valor é .0000000000001 menor que o primeiro.
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
Nos casos em que uma perda de precisão provavelmente afetará o resultado de uma comparação, você pode usar as seguintes técnicas em vez de chamar o Equals método ou CompareTo :
Chame o Math.Round método para garantir que ambos os valores tenham a mesma precisão. O exemplo a seguir modifica um exemplo anterior para usar essa abordagem para que dois valores fracionários sejam equivalentes.
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
O problema da precisão ainda se aplica ao arredondamento dos valores médios. Para obter mais informações, consulte o método Math.Round(Double, Int32, MidpointRounding).
Teste de igualdade aproximada em vez de igualdade. Essa técnica requer que você defina uma quantidade absoluta pela qual os dois valores podem diferir, mas ainda assim serem iguais, ou que você defina uma quantidade relativa pela qual o valor menor pode divergir do valor maior.
Aviso
Single.Epsilon às vezes é usado como uma medida absoluta da distância entre dois Single valores ao testar a igualdade. No entanto, Single.Epsilon mede o menor valor possível que pode ser adicionado ou subtraído de um Single cujo valor é zero. Para a maioria dos valores positivos e negativos Single , o valor de Single.Epsilon é muito pequeno para ser detectado. Portanto, com exceção dos valores que são zero, não recomendamos seu uso em testes de igualdade.
O exemplo a seguir usa a última abordagem para definir um
IsApproximatelyEqual
método que testa a diferença relativa entre dois valores. Ele também contrasta o resultado das chamadas com o método e oIsApproximatelyEqual
Equals(Single) método.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
Valores de ponto flutuante e exceções
Operações com valores de ponto flutuante não lançam exceções, ao contrário das operações com tipos integrais, que lançam exceções em casos de operações ilegais, como divisão por zero ou transbordamento. Em vez disso, nessas situações, o resultado de uma operação de ponto flutuante é zero, infinito positivo, infinito negativo ou não um número (NaN):
Se o resultado de uma operação de ponto flutuante for muito pequeno para o formato de destino, o resultado será zero. Isso pode ocorrer quando dois números de ponto flutuante muito pequenos são multiplicados, como mostra o exemplo a seguir.
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
Se a magnitude do resultado de uma operação de ponto flutuante exceder o intervalo do formato de destino, o resultado da operação será PositiveInfinity ou NegativeInfinity, conforme apropriado para o sinal do resultado. O resultado de uma operação que estoura é , e o resultado de uma operação que estoura Single.MaxValueSingle.MinValue é PositiveInfinityNegativeInfinity, como mostra o exemplo a seguir.
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 também resulta de uma divisão por zero com um dividendo positivo, e NegativeInfinity resulta de uma divisão por zero com um dividendo negativo.
Se uma operação de ponto flutuante for inválida, o resultado da operação será NaN. Por exemplo, NaN resultados das seguintes operações:
- Divisão por zero com dividendo zero. Note que outros casos de divisão por zero resultam em um PositiveInfinity ou NegativeInfinityoutro .
- Qualquer operação de ponto flutuante com entrada inválida. Por exemplo, tentar localizar a raiz quadrada de um valor negativo retorna NaN.
- Qualquer operação com um argumento cujo valor seja Single.NaN.
Conversões de tipo
A Single estrutura não define nenhum operador de conversão explícito ou implícito, em vez disso, as conversões são implementadas pelo compilador.
A tabela a seguir lista as possíveis conversões de um valor dos outros tipos numéricos primitivos para um Single valor, Ele também indica se a conversão está aumentando ou estreitando e se o resultado Single pode ter menos precisão do que o valor original.
Conversão de | Alargamento/estreitamento | Possível perda de precisão |
---|---|---|
Byte | Widening | Não |
Decimal | Widening Observe que o C# requer um operador de transmissão. |
Sim. Decimal suporta 29 dígitos decimais de precisão; Single suporta 9. |
Double | Estreitamento; valores fora do intervalo são convertidos em Double.NegativeInfinity ou Double.PositiveInfinity. | Sim. Double suporta 17 dígitos decimais de precisão; Single suporta 9. |
Int16 | Widening | Não |
Int32 | Widening | Sim. Int32 suporta 10 dígitos decimais de precisão; Single suporta 9. |
Int64 | Widening | Sim. Int64 suporta 19 dígitos decimais de precisão; Single suporta 9. |
SByte | Widening | Não |
UInt16 | Widening | Não |
UInt32 | Widening | Sim. UInt32 suporta 10 dígitos decimais de precisão; Single suporta 9. |
UInt64 | Widening | Sim. Int64 suporta 20 dígitos decimais de precisão; Single suporta 9. |
O exemplo a seguir converte o valor mínimo ou máximo de outros tipos numéricos primitivos em um Single valor.
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)
Além disso, os Double valores Double.NaN, e convertem em Single.NaN, e Double.NegativeInfinitySingle.NegativeInfinity, Double.PositiveInfinitySingle.PositiveInfinityrespectivamente.
Observe que a conversão do valor de alguns tipos numéricos em um Single valor pode envolver uma perda de precisão. Como o exemplo ilustra, uma perda de precisão é possível ao converter Decimal, Double, , , Int32UInt32Int64e UInt64 valores em Single valores.
A conversão de um valor em um SingleDouble é uma conversão de ampliação. A conversão pode resultar em uma perda de precisão se o tipo não tiver uma representação precisa para o DoubleSingle valor.
A conversão de um valor em um valor de qualquer tipo de dados numérico primitivo diferente de a Double é uma conversão de estreitamento e requer um operador cast (em C#) ou um Single método de conversão (em Visual Basic). Os valores que estão fora do intervalo do tipo de dados de destino, que são definidos pelas propriedades e MaxValue
do tipo de destino, comportam-se MinValue
conforme mostrado na tabela a seguir.
Tipo de destino | Resultado |
---|---|
Qualquer tipo integral | Uma OverflowException exceção se a conversão ocorrer em um contexto verificado. Se a conversão ocorrer em um contexto não verificado (o padrão em C#), a operação de conversão será bem-sucedida, mas o valor estourará. |
Decimal | Uma OverflowException exceção, |
Além disso, , e Single.NegativeInfinity lançar um para conversões em inteiros em um contexto verificado, Single.NaNSingle.PositiveInfinitymas esses valores transbordam quando convertidos em inteiros em um OverflowException contexto não verificado. Para conversões para Decimal, eles sempre lançam um OverflowExceptionarquivo . Para conversões em , elas convertem em DoubleDouble.NaN, e Double.NegativeInfinity, Double.PositiveInfinityrespectivamente.
Observe que uma perda de precisão pode resultar da conversão de um Single valor em outro tipo numérico. No caso de converter valores não integrais Single , como mostra a saída do exemplo, o componente fracionário é perdido quando o Single valor é arredondado (como no Visual Basic) ou truncado (como em C# e F#). Para conversões em Decimal valores, o Single valor pode não ter uma representação precisa no tipo de dados de destino.
O exemplo a seguir converte vários Single valores em vários outros tipos numéricos. As conversões ocorrem em um contexto verificado no Visual Basic (o padrão), em C# (devido à palavra-chave verificada) e em F# (devido à open Checked
instrução). A saída do exemplo mostra o resultado para conversões em um contexto verificado e não verificado. Você pode executar conversões em um contexto não verificado no Visual Basic compilando com a opção do compilador, em C# comentando a instrução e em F# comentando a /removeintchecks+
checked
open Checked
instrução.
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)
Para obter mais informações sobre a conversão de tipos numéricos, consulte Conversão de tipo no .NET e Tabelas de conversão de tipo.
Funcionalidade de ponto flutuante
A Single estrutura e os tipos relacionados fornecem métodos para executar as seguintes categorias de operações:
Comparação de valores. Você pode chamar o método para determinar se dois valores são iguais ou o EqualsCompareTo método para determinar a relação entre dois Single valores.
A Single estrutura também suporta um conjunto completo de operadores de comparação. Por exemplo, você pode testar a igualdade ou a desigualdade ou determinar se um valor é maior ou igual a outro valor. Se um dos operandos for um , o Single valor será convertido em um DoubleDouble antes de executar a comparação. Se um dos operandos for um tipo integral, ele será convertido em um Single antes de realizar a comparação. Embora sejam conversões crescentes, elas podem envolver uma perda de precisão.
Aviso
Devido a diferenças na precisão, dois Single valores que você espera que sejam iguais podem se tornar desiguais, o que afeta o resultado da comparação. Consulte a seção Testar para igualdade para obter mais informações sobre como comparar dois Single valores.
Você também pode chamar os IsNaNmétodos , IsInfinity, IsPositiveInfinitye IsNegativeInfinity para testar esses valores especiais.
Operações matemáticas. Operações aritméticas comuns, como adição, subtração, multiplicação e divisão, são implementadas por compiladores de linguagem e instruções Common Intermediate Language (CIL) em vez de métodos Single . Se o outro operando em uma operação matemática for um , o é convertido em um antes de executar a operação, e o Single resultado da operação também é um DoubleDoubleDouble valor. Se o outro operando for um tipo integral, ele será convertido em um antes de executar a operação, e o resultado da operação também será um SingleSingle valor.
Você pode executar outras operações matemáticas chamando
static
(Shared
no Visual Basic) métodos na System.Math classe. Estes incluem métodos adicionais comumente usados para aritmética (como , e ), geometria (como e ) e Math.SinMath.Sqrtcálculo (como Math.AbsMath.CosMath.Log). Math.Sign Em todos os casos, o Single valor é convertido em um Doublearquivo .Você também pode manipular os bits individuais em um Single valor. O BitConverter.GetBytes(Single) método retorna seu padrão de bits em uma matriz de bytes. Ao passar essa matriz de bytes para o método, você também pode preservar o BitConverter.ToInt32 padrão de bits do Single valor em um inteiro de 32 bits.
Arredondamento. O arredondamento é frequentemente usado como uma técnica para reduzir o impacto das diferenças entre os valores causadas por problemas de representação e precisão do ponto flutuante. Você pode arredondar um Single valor chamando o Math.Round método. No entanto, observe que o valor é convertido em um Double antes que o Single método seja chamado, e a conversão pode envolver uma perda de precisão.
Formatação. Você pode converter um Single valor em sua representação de cadeia de caracteres chamando o método ou usando o ToStringrecurso de formatação composta. Para obter informações sobre como as cadeias de caracteres de formato controlam a representação de cadeias de caracteres de valores de ponto flutuante, consulte os tópicos Cadeias de caracteres de formato numérico padrão e Cadeias de caracteres de formato numérico personalizado.
Analisando cadeias de caracteres. Você pode converter a representação de cadeia de caracteres de um valor de ponto flutuante em um Single valor chamando o Parse método or TryParse . Se a operação de análise falhar, o método lançará uma exceção, enquanto o ParseTryParse método retornará
false
.Conversão de tipos. A Single estrutura fornece uma implementação de interface explícita para a IConvertible interface, que oferece suporte à conversão entre quaisquer dois tipos de dados .NET padrão. Os compiladores de linguagem também suportam a conversão implícita de valores para todos os outros tipos numéricos padrão, exceto para a conversão de Double valores Single . A conversão de um valor de qualquer tipo numérico padrão que não seja a para a DoubleSingle é uma conversão de alargamento e não requer o uso de um operador de fundição ou método de conversão.
No entanto, a conversão de valores inteiros de 32 bits e 64 bits pode envolver uma perda de precisão. A tabela a seguir lista as diferenças de precisão para 32 bits, 64 bits e Double tipos:
Tipo Máxima precisão (em dígitos decimais) Precisão interna (em dígitos decimais) Double 15 17 Int32 e UInt32 10 10 Int64 e UInt64 19 19 Single 7 9 O problema da precisão afeta Single mais frequentemente os valores que são convertidos em Double valores. No exemplo a seguir, dois valores produzidos por operações de divisão idênticas são desiguais, porque um dos valores é um valor de ponto flutuante de precisão única que é convertido em um Doublearquivo .
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