System.Estrutura única

Observação

Este artigo fornece observações complementares à documentação de referência para esta API.

O tipo de valor Single representa um número de 32 bits de precisão única com valores que variam de 3,402823e38 negativos a 3,402823e38 positivos, 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 tipo Single está em conformidade com a norma IEC 60559:1989 (IEEE 754) para aritmética binária de ponto flutuante.

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ãoe Cadeias de caracteres de formato numérico personalizado.

Representação e precisão de ponto flutuante

O tipo de dados Single 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. Neste caso, o valor de vírgula flutuante fornece uma representação imprecisa do número que representa. A realização de operações matemáticas adicionais sobre o valor original de vírgula flutuante muitas vezes 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 apenas se forem exibidos os dois valores Single usando a cadeia de formato numérico padrão "R" , que, se necessário, exibe todos os nove dígitos de precisão suportados pelo tipo Single.

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:           {result1:R}");
        Console.WriteLine($".2 Added 10 times: {result2:R}");
    }
}
// The example displays the following output:
//       .2 * 10:           2
//       .2 Added 10 times: 2.0000002
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.0000002
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.0000002

Como alguns números não podem ser representados exatamente como valores binários fracionários, os números de vírgula flutuante só podem se aproximar de números reais.

Todos os números de vírgula flutuante têm um número limitado de dígitos significativos, o que também determina a precisão com que um valor de vírgula flutuante se aproxima de um número real. Um valor Single 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 é demasiado pequeno para modificar o valor em ponto flutuante original. Seu dígito menos significativo é milésimo, 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 imprecisão de um número de ponto flutuante tem várias consequências:

  • Dois números de vírgula flutuante que parecem iguais para uma determinada precisão podem não ser iguais porque seus dígitos menos significativos são diferentes. No exemplo a seguir, uma série de números é somada e seu total é comparado com o total esperado. Uma chamada ao Equals método indica que os valores não são iguais.

    using System;
    
    public class PrecisionList3Example
    {
        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 ({total}) does not equal the total ({result}).");
        }
    }
    
    // The example displays the following output on .NET:
    //      The sum of the values (27.650002) does not equal the total (27.65).
    // The example displays the following output on .NET Framework:
    //      The sum of the values (27.65) 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 on .NET:
    //      The sum of the values (27.650002) does not equal the total (27.65).
    // The example displays the following output on .NET Framework:
    //      The sum of the values (27.65) does not equal the total (27.65).
    
        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 ({total}) does not equal the total ({result}).")
        End If
    End Sub
    
    ' The example displays the following output on .NET:
    '      The sum of the values (27.650002) does not equal the total (27.65).
    ' The example displays the following output on .NET Framework:
    '      The sum of the values (27.65) does not equal the total (27.65).
    

    Os dois valores são desiguais devido à perda de precisão durante as operações de adição. Nesse caso, o problema pode ser resolvido chamando o método Math.Round(Double, Int32) para arredondar os valores de Single para a precisão desejada antes de executar a comparação.

  • Uma operação matemática ou de comparação que usa um número de vírgula flutuante pode não produzir o mesmo resultado se um número decimal for usado, porque o número binário de vírgula flutuante 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 tipo Decimal em vez do tipo Single. Quando a precisão em operações numéricas com valores integrais além do intervalo dos tipos Int64 ou UInt64 for importante, use o tipo BigInteger.

  • Um valor pode não de ida e volta se um número de vírgula flutuante estiver envolvido. Diz-se que um valor é de ida e volta se uma operação converte um número de vírgula flutuante original em outra forma, uma operação inversa transforma a forma convertida de volta em um número de vírgula flutuante e o número final de vírgula flutuante é igual ao número de vírgula 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 valores de Single são convertidos em cadeias de caracteres e salvos em um arquivo. Se você executar este exemplo no .NET Framework, mesmo que os valores pareçam ser idênticos, os valores restaurados não são iguais aos valores originais. (Isso já foi abordado no .NET, onde os valores de ida e volta são corretos.)

    StreamWriter sw = new(@"./Singles.dat");
    float[] 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();
    
    float[] restoredValues = new float[values.Length];
    StreamReader sr = new(@"./Singles.dat");
    string temp = sr.ReadToEnd();
    string[] tempStrings = temp.Split('|');
    for (int ctr = 0; ctr < tempStrings.Length; ctr++)
        restoredValues[ctr] = float.Parse(tempStrings[ctr]);
    
    for (int ctr = 0; ctr < values.Length; ctr++)
        Console.WriteLine($"{values[ctr]} {(values[ctr].Equals(restoredValues[ctr]) ? "=" : "<>")} {restoredValues[ctr]}");
    
    // The example displays the following output on .NET Framework:
    //       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 on .NET Framework:
    //       2.882883 <> 2.882883
    //       0.3333333 <> 0.3333333
    //       3.141593 <> 3.141593
    
    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
    
    ' The example displays the following output on .NET Framework:
    '        2.882883 <> 2.882883
    '        0.3333333 <> 0.3333333
    '        3.141593 <> 3.141593
    

    Se você estiver direcionando o .NET Framework, 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 valores Single.

  • Single valores têm menos precisão do que Double valores. Um valor Single que é convertido em um Double aparentemente equivalente muitas vezes não é igual ao valor Double 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 Double e a um valor Single. Depois que o valor Single é 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($"{value1:R} = {value2:R}: {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 Double no lugar do tipo de dados Single ou use o método Round para que ambos os valores tenham a mesma precisão.

Teste de igualdade

Para serem considerados iguais, dois valores Single devem representar valores idênticos. No entanto, devido a diferenças de precisão entre os valores, ou devido a uma perda de precisão por um ou ambos os valores, os valores de vírgula 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 Equals para determinar se dois valores são iguais, ou chamadas para o método CompareTo para determinar a relação entre dois valores Single, geralmente produzem resultados inesperados. Isto é evidente no exemplo seguinte, onde dois valores Single aparentemente iguais acabam por ser desiguais, porque 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($"{value1:R} = {value2:R}: {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 valor Single é quadrado e, em seguida, a raiz quadrada é calculada para restaurar o valor original. Um segundo Single é multiplicado por 3,51 e quadrado antes de dividir a raiz quadrada do resultado por 3,51 para restaurar o valor original. Embora os dois valores pareçam ser idênticos, uma chamada para o método Equals(Single) indica que eles não são iguais.

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($"{value1} = {value2}: {value1.Equals(value2)}");

// The example displays the following output on .NET:
//       10.201438 = 10.201439: False
// The example displays the following output on .NET Framework:
//       10.20144 = 10.20144: False
let value1 =
    10.201438f ** 2f
    |> sqrt

let value2 =
   ((value1 * 3.51f) ** 2f |> sqrt) / 3.51f

printfn $"{value1} = {value2}: {value1.Equals value2}"

// The example displays the following output on .NET:
//       10.201438 = 10.201439: False
// The example displays the following output on .NET Framework:
//       10.20144 = 10.20144: False
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))

' The example displays the following output on .NET:
'       10.201438 = 10.201439: False
' The example displays the following output on .NET Framework:
'       10.20144 = 10.20144: False

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 método Equals ou CompareTo:

  • Chame o método Math.Round 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.

    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($"{value1:R} = {value2:R}: {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 continua a aplicar-se ao arredondamento dos valores médios. Para obter mais informações, consulte o método Math.Round(Double, Int32, MidpointRounding).

  • Teste para 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 ser iguais, ou que você defina uma quantidade relativa pela qual o valor menor pode divergir do valor maior.

    Advertência

    Single.Epsilon às vezes é usado como uma medida absoluta da distância entre dois valores Single 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 de Single positivos e negativos, o valor de Single.Epsilon é muito pequeno para ser detetado. Portanto, exceto para 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 método IsApproximatelyEqual que testa a diferença relativa entre dois valores. Ele também contrasta o resultado de chamadas para o método IsApproximatelyEqual e o método Equals(Single).

    public static void Main()
    {
        float one1 = .1f * 10;
        float one2 = 0f;
        for (int ctr = 1; ctr <= 10; ctr++)
            one2 += .1f;
    
        Console.WriteLine($"{one1:R} = {one2:R}: {one1.Equals(one2)}");
        Console.WriteLine($"{one1:R} is approximately equal to {one2:R}: " +
            $"{IsApproximatelyEqual(one1, one2, .000001f)}");
    
        float negativeOne1 = -1 * one1;
        float negativeOne2 = -1 * one2;
    
        Console.WriteLine($"{negativeOne1:R} = {negativeOne2:R}: {negativeOne1.Equals(negativeOne2)}");
        Console.WriteLine($"{negativeOne1:R} is approximately equal to {negativeOne2:R}: " +
            $"{IsApproximatelyEqual(negativeOne1, negativeOne2, .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 on .NET:
    //       1 = 1.0000001: False
    //       1 is approximately equal to 1.0000001: True
    //       -1 = -1.0000001: False
    //       -1 is approximately equal to -1.0000001: 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.0000001: False
    //       1 is approximately equal to 1.0000001: True
    
    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
    
    ' The example displays the following output:
    '       1 = 1.0000001: False
    '       1 is approximately equal to 1.0000001: True
    

Valores de vírgula 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 overflow. 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 vírgula flutuante muito pequenos são multiplicados, como mostra o exemplo a seguir.

    float value1 = 1.163287e-36f;
    float value2 = 9.164234e-25f;
    float result = value1 * value2;
    Console.WriteLine($"{value1} * {value2} = {result}");
    Console.WriteLine($"{result} = 0: {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 a gama do formato de destino, o resultado da operação é PositiveInfinity ou NegativeInfinity, conforme apropriado ao sinal do resultado. O resultado de uma operação que transborda Single.MaxValue é PositiveInfinitye o resultado de uma operação que transborda Single.MinValue é NegativeInfinity, como mostra o exemplo a seguir.

    float value1 = 3.065e35f;
    float value2 = 6.9375e32f;
    float result = value1 * value2;
    Console.WriteLine($"PositiveInfinity: {Single.IsPositiveInfinity(result)}");
    Console.WriteLine($"NegativeInfinity: {Single.IsNegativeInfinity(result)}");
    Console.WriteLine();
    
    value1 = -value1;
    result = value1 * value2;
    Console.WriteLine($"PositiveInfinity: {Single.IsPositiveInfinity(result)}");
    Console.WriteLine($"NegativeInfinity: {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 resulta das seguintes operações:

    • Divisão por zero com um dividendo de zero. Note-se que outros casos de divisão por zero resultam em PositiveInfinity ou NegativeInfinity.
    • Qualquer operação de ponto flutuante com entrada inválida. Por exemplo, tentar encontrar a raiz quadrada de um valor negativo retorna NaN.
    • Qualquer operação com um argumento cujo valor é Single.NaN.

Conversões de tipos

A estrutura Single 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 seguinte lista as possíveis conversões de um valor dos outros tipos numéricos primitivos para um Single valor. Indica também se a conversão está a alargar ou a estreitar 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 Alargamento Não
Decimal Alargamento

Observe que o C# requer um operador cast.
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 Alargamento Não
Int32 Alargamento Sim. Int32 suporta 10 dígitos decimais de precisão; Single suporta 9.
Int64 Alargamento Sim. Int64 suporta 19 dígitos decimais de precisão; Single suporta 9.
SByte Alargamento Não
UInt16 Alargamento Não
UInt32 Alargamento Sim. UInt32 suporta 10 dígitos decimais de precisão; Single suporta 9.
UInt64 Alargamento 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 valor Single.

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($"{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)
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 valores DoubleDouble.NaN, Double.PositiveInfinitye Double.NegativeInfinity convertem em Single.NaN, Single.PositiveInfinitye Single.NegativeInfinity, respectivamente.

Observe que a conversão do valor de alguns tipos numéricos para um valor Single pode envolver uma perda de precisão. Como o exemplo ilustra, uma perda de precisão é possível ao converter Decimal, Double, Int32, Int64, UInt32e UInt64 valores em valores Single.

A conversão de um valor de Single em um Double é uma conversão crescente. A conversão pode resultar em uma perda de precisão se o tipo Double não tiver uma representação precisa para o valor Single.

A conversão de um valor Single para um valor de qualquer tipo de dados numéricos primitivos que não seja um Double é uma conversão de estreitamento e requer um operador cast (em C#) ou um 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 MinValue e MaxValue do tipo de destino, se comportam conforme mostrado na tabela a seguir.

Tipo de alvo Resultado
Qualquer tipo integral Uma exceção OverflowException se a conversão ocorrer em um contexto verificado.

Se a conversão ocorrer num contexto sem verificação (o padrão em C#), a operação de conversão é bem-sucedida, mas o valor excede.
Decimal Uma OverflowException exceção.

Além disso, Single.NaN, Single.PositiveInfinitye Single.NegativeInfinity lançam uma exceção OverflowException para conversões em inteiros em um contexto verificado, mas esses valores comportam-se de forma inesperada quando convertidos em inteiros em um contexto não verificado. Para conversões para Decimal, eles sempre lançam um OverflowException. Para conversões para Double, convertem-se em Double.NaN, Double.PositiveInfinitye Double.NegativeInfinity, respectivamente.

Observe que uma perda de precisão pode resultar da conversão de um valor Single para outro tipo numérico. No caso da conversão de valores de Single não integrais, como mostra a saída do exemplo, o componente fracionário é perdido quando o valor Single é arredondado (como no Visual Basic) ou truncado (como em C# e F#). Para conversões para valores Decimal, o valor Single pode não ter uma representação precisa no tipo de dados de destino.

O exemplo a seguir converte vários valores de Single em vários outros tipos numéricos. As conversões ocorrem num contexto verificado no Visual Basic (o padrão), em C# (por causa da palavra-chave checked) e em F# (por causa da declaração open Checked). A saída do exemplo mostra o resultado de 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 de compilador /removeintchecks+, em C# comentando a instrução checked e em F# comentando a instrução open Checked.

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($"{value} ({value.GetType().Name}) --> {lValue} (0x{lValue:X16}) ({lValue.GetType().Name})");
        }
        catch (OverflowException)
        {
            Console.WriteLine($"Unable to convert {value} to Int64.");
        }
        try
        {
            UInt64 ulValue = (ulong)value;
            Console.WriteLine($"{value} ({value.GetType().Name}) --> {ulValue} (0x{ulValue:X16}) ({ulValue.GetType().Name})");
        }
        catch (OverflowException)
        {
            Console.WriteLine($"Unable to convert {value} to UInt64.");
        }
        try
        {
            Decimal dValue = (decimal)value;
            Console.WriteLine($"{value} ({value.GetType().Name}) --> {dValue} ({dValue.GetType().Name})");
        }
        catch (OverflowException)
        {
            Console.WriteLine($"Unable to convert {value} to Decimal.");
        }

        Double dblValue = value;
        Console.WriteLine($"{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 ∞ to Int64.
//       Unable to convert ∞ to UInt64.
//       Unable to convert ∞ to Decimal.
//       ∞ (Single) --> ∞ (Double)
//
//       Unable to convert -∞ to Int64.
//       Unable to convert -∞ to UInt64.
//       Unable to convert -∞ to Decimal.
//       -∞ (Single) --> -∞ (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 ∞ to Int64.
//       Unable to convert ∞ to UInt64.
//       Unable to convert ∞ to Decimal.
//       ∞ (Single) --> ∞ (Double)
//
//       Unable to convert -∞ to Int64.
//       Unable to convert -∞ to UInt64.
//       Unable to convert -∞ to Decimal.
//       -∞ (Single) --> -∞ (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 ∞ to Int64.
'       Unable to convert ∞ to UInt64.
'       Unable to convert ∞ to Decimal.
'       ∞ (Single) --> ∞ (Double)
'
'       Unable to convert -∞ to Int64.
'       Unable to convert -∞ to UInt64.
'       Unable to convert -∞ to Decimal.
'       -∞ (Single) --> -∞ (Double)

Para obter mais informações sobre a conversão de tipos numéricos, consulte Conversão de Tipos no .NET e Tabelas de Conversão de Tipos.

Funcionalidade de ponto flutuante

A estrutura Single 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 Equals para determinar se dois valores Single são iguais ou o método CompareTo para determinar a relação entre dois valores.

    A estrutura Single 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 Double, o valor Single será convertido em um Double antes de executar a comparação. Se um dos operandos for um tipo integral, ele será convertido em um Single antes de executar a comparação. Embora estas sejam conversões crescentes, podem envolver uma perda de precisão.

    Advertência

    Devido a diferenças na precisão, dois Single valores que você espera que sejam iguais podem acabar sendo desiguais, o que afeta o resultado da comparação. Consulte a secção sobre o Teste de igualdade para obter mais informações sobre como comparar dois valores Single.

    Você também pode chamar os métodos IsNaN, 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 de linguagem intermediária comum (CIL) em vez de por métodos Single. Se o outro operando em uma operação matemática é um Double, o Single é convertido em um Double antes de executar a operação, e o resultado da operação também é um valor Double. Se o outro operando for um tipo integral, ele será convertido em um Single antes de executar a operação, e o resultado da operação também será um valor Single.

    Você pode executar outras operações matemáticas chamando static (Shared no Visual Basic) métodos na classe System.Math. Estes incluem métodos adicionais comumente usados para aritmética (como Math.Abs, Math.Signe Math.Sqrt), geometria (como Math.Cos e Math.Sin) e cálculo (como Math.Log). Em todos os casos, o valor Single é convertido em um Double.

    Você também pode manipular os bits individuais em um valor Single. O método BitConverter.GetBytes(Single) retorna seu padrão de bits em uma matriz de bytes. Ao passar essa matriz de bytes para o método BitConverter.ToInt32, você também pode preservar o padrão de bits do valor Single em um inteiro de 32 bits.

  • Arredondamento. O arredondamento é frequentemente usado como uma técnica para reduzir o impacto das diferenças entre valores causadas por problemas de representação e precisão de ponto flutuante. Você pode arredondar um valor Single chamando o método Math.Round. No entanto, observe que o valor Single é convertido em um Double antes do método ser chamado, e a conversão pode envolver uma perda de precisão.

  • Formatação. Você pode converter um valor de Single em sua representação de cadeia de caracteres chamando o método ToString ou usando o recurso 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 vírgula flutuante, consulte Cadeias de caracteres de formato numérico padrão e Cadeias de caracteres de formato numérico personalizadas.

  • Analisar cadeias de caracteres. Você pode converter a representação de cadeia de caracteres de um valor de ponto flutuante em um valor Single chamando o método Parse ou TryParse. Se a operação de análise falhar, o método Parse lançará uma exceção, enquanto o método TryParse retornará false.

  • Conversão de tipo. A estrutura Single fornece uma implementação de interface explícita para a interface IConvertible, que suporta a 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 em valores Single. A conversão de um valor de qualquer tipo numérico normalizado que não seja um Double para um Single é uma conversão alargada e não requer a utilização de um operador de fundição ou de um 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 os tipos de 32 bits, 64 bits e Double:

    Tipo Precisão máxima (dígitos decimais) Precisão interna (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 com mais frequência os valores Single que são convertidos em valores Double. 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 Double.

    Double value1 = 1 / 3.0;
    Single sValue2 = 1 / 3.0f;
    Double value2 = (Double)sValue2;
    Console.WriteLine($"{value1:R} = {value2:R}: {value1.Equals(value2)}");
    
    // The example displays the following output on .NET:
    //        0.3333333333333333 = 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 on .NET:
    //     0.3333333333333333 = 0.3333333432674408: False
    
    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))
    
    ' The example displays the following output:
    '       0.3333333333333333 = 0.3333333432674408: False