System.Double struct

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

O Double tipo de valor representa um número de 64 bits de precisão dupla com valores que variam de 1,79769313486232e308 negativo a 1,79769313486232e308, 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 Double tipo está em conformidade com o padrão IEC 60559:1989 (IEEE 754) para aritmética de ponto flutuante binário.

Representação de ponto flutuante e precisão

O Double tipo de dados armazena valores de ponto flutuante de precisão dupla em um formato binário de 64 bits, conforme mostrado na tabela a seguir:

Parte Bits
Significand ou mantissa 0-51
Expoente 52-62
Sinal (0 = Positivo, 1 = Negativo) 63

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, 1/10, que é representado precisamente por .1 como uma fração decimal, é representado por .001100110011 como uma fração binária, com o padrão "0011" repetindo ao infinito. Nesse caso, o valor de ponto flutuante fornece uma representação imprecisa do número que ele representa. A realização de operações matemáticas adicionais no valor original do ponto flutuante geralmente tende a aumentar sua falta de precisão. Por exemplo, se compararmos o resultado de multiplicar .1 por 10 e adicionar .1 a .1 nove vezes, veremos que a adição, por ter envolvido mais oito operações, produziu o resultado menos preciso. Observe que essa disparidade é aparente somente se exibirmos os dois Double valores usando a cadeia de caracteres de formato numérico padrão "R", que se necessário exibe todos os 17 dígitos de precisão suportados Double pelo tipo.

using System;

public class Example13
{
    public static void Main()
    {
        Double value = .1;
        Double result1 = value * 10;
        Double result2 = 0;
        for (int ctr = 1; ctr <= 10; ctr++)
            result2 += value;

        Console.WriteLine(".1 * 10:           {0:R}", result1);
        Console.WriteLine(".1 Added 10 times: {0:R}", result2);
    }
}
// The example displays the following output:
//       .1 * 10:           1
//       .1 Added 10 times: 0.99999999999999989
let value = 0.1
let result1 = value * 10.
let mutable result2 = 0.
for i = 1 to 10 do
    result2 <- result2 + value

printfn $".1 * 10:           {result1:R}"
printfn $".1 Added 10 times: {result2:R}"
// The example displays the following output:
//       .1 * 10:           1
//       .1 Added 10 times: 0.99999999999999989
Module Example14
    Public Sub Main()
        Dim value As Double = 0.1
        Dim result1 As Double = value * 10
        Dim result2 As Double
        For ctr As Integer = 1 To 10
            result2 += value
        Next
        Console.WriteLine(".1 * 10:           {0:R}", result1)
        Console.WriteLine(".1 Added 10 times: {0:R}", result2)
    End Sub
End Module
' The example displays the following output:
'       .1 * 10:           1
'       .1 Added 10 times: 0.99999999999999989

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 também 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 Double valor tem até 15 dígitos decimais de precisão, embora um máximo de 17 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 ilustra esse cenário. Ele define um valor de ponto flutuante muito grande e, em seguida, adiciona o produto de Double.Epsilon e um quatrilhão a ele. O produto, no entanto, é 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-309.

using System;

public class Example14
{
    public static void Main()
    {
        Double value = 123456789012.34567;
        Double additional = Double.Epsilon * 1e15;
        Console.WriteLine("{0} + {1} = {2}", value, additional,
                                             value + additional);
    }
}
// The example displays the following output:
//    123456789012.346 + 4.94065645841247E-309 = 123456789012.346
open System

let value = 123456789012.34567
let additional = Double.Epsilon * 1e15
printfn $"{value} + {additional} = {value + additional}"
// The example displays the following output:
//    123456789012.346 + 4.94065645841247E-309 = 123456789012.346
Module Example15
    Public Sub Main()
        Dim value As Double = 123456789012.34567
        Dim additional As Double = Double.Epsilon * 1.0E+15
        Console.WriteLine("{0} + {1} = {2}", value, additional,
                                           value + additional)
    End Sub
End Module
' The example displays the following output:
'   123456789012.346 + 4.94065645841247E-309 = 123456789012.346

A 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 Example10
    {
        public static void Main()
        {
            Double[] values = { 10.0, 2.88, 2.88, 2.88, 9.0 };
            Double result = 27.64;
            Double total = 0;
            foreach (var value in values)
                total += value;
    
            if (total.Equals(result))
                Console.WriteLine("The sum of the values equals the total.");
            else
                Console.WriteLine("The sum of the values ({0}) does not equal the total ({1}).",
                                  total, result);
        }
    }
    // The example displays the following output:
    //      The sum of the values (36.64) does not equal the total (36.64).
    //
    // If the index items in the Console.WriteLine statement are changed to {0:R},
    // the example displays the following output:
    //       The sum of the values (27.639999999999997) does not equal the total (27.64).
    
    let values = [ 10.0; 2.88; 2.88; 2.88; 9.0 ]
    let result = 27.64
    let total = List.sum values
    
    if total.Equals result then
        printfn "The sum of the values equals the total."
    else
        printfn $"The sum of the values ({total}) does not equal the total ({result})."
    // The example displays the following output:
    //      The sum of the values (36.64) does not equal the total (36.64).
    //
    // If the index items in the Console.WriteLine statement are changed to {0:R},
    // the example displays the following output:
    //       The sum of the values (27.639999999999997) does not equal the total (27.64).
    
    Module Example11
        Public Sub Main()
            Dim values() As Double = {10.0, 2.88, 2.88, 2.88, 9.0}
            Dim result As Double = 27.64
            Dim total As Double
            For Each value In values
                total += value
            Next
            If total.Equals(result) Then
                Console.WriteLine("The sum of the values equals the total.")
            Else
                Console.WriteLine("The sum of the values ({0}) does not equal the total ({1}).",
                               total, result)
            End If
        End Sub
    End Module
    ' The example displays the following output:
    '      The sum of the values (36.64) does not equal the total (36.64).   
    '
    ' If the index items in the Console.WriteLine statement are changed to {0:R},
    ' the example displays the following output:
    '       The sum of the values (27.639999999999997) does not equal the total (27.64).
    

    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 Double 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 Double 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 .1 por 10 e adicionar .1 vezes.

    Quando a precisão em operações numéricas com valores fracionários é importante, você pode usar o Decimal tipo em vez do Double 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 final de ponto flutuante não é 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 Double valores são convertidos em cadeias de caracteres e salvos em um arquivo. Como a saída mostra, no entanto, mesmo que 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 Example11
    {
        public static void Main()
        {
            StreamWriter sw = new StreamWriter(@".\Doubles.dat");
            Double[] values = { 2.2 / 1.01, 1.0 / 3, Math.PI };
            for (int ctr = 0; ctr < values.Length; ctr++)
            {
                sw.Write(values[ctr].ToString());
                if (ctr != values.Length - 1)
                    sw.Write("|");
            }
            sw.Close();
    
            Double[] restoredValues = new Double[values.Length];
            StreamReader sr = new StreamReader(@".\Doubles.dat");
            string temp = sr.ReadToEnd();
            string[] tempStrings = temp.Split('|');
            for (int ctr = 0; ctr < tempStrings.Length; ctr++)
                restoredValues[ctr] = Double.Parse(tempStrings[ctr]);
    
            for (int ctr = 0; ctr < values.Length; ctr++)
                Console.WriteLine("{0} {2} {1}", values[ctr],
                                  restoredValues[ctr],
                                  values[ctr].Equals(restoredValues[ctr]) ? "=" : "<>");
        }
    }
    // The example displays the following output:
    //       2.17821782178218 <> 2.17821782178218
    //       0.333333333333333 <> 0.333333333333333
    //       3.14159265358979 <> 3.14159265358979
    
    open System
    open System.IO
    
    let values = [ 2.2 / 1.01; 1. / 3.; Math.PI ]
    
    using (new StreamWriter(@".\Doubles.dat")) (fun sw ->
        for i = 0 to values.Length - 1 do
            sw.Write(string values[i])
            if i <> values.Length - 1 then
                sw.Write "|")
    
    using (new StreamReader(@".\Doubles.dat")) (fun sr ->
        let temp = sr.ReadToEnd()
        let tempStrings = temp.Split '|'
    
        let restoredValues =
            [ for i = 0 to tempStrings.Length - 1 do
                  Double.Parse tempStrings[i] ]
    
        for i = 0 to values.Length - 1 do
            printfn $"""{values[i]} {if values[ i ].Equals restoredValues[i] then "=" else "<>"} {restoredValues[i]}""")
    
    // The example displays the following output:
    //       2.17821782178218 <> 2.17821782178218
    //       0.333333333333333 <> 0.333333333333333
    //       3.14159265358979 <> 3.14159265358979
    
    Imports System.IO
    
    Module Example12
        Public Sub Main()
            Dim sw As New StreamWriter(".\Doubles.dat")
            Dim values() As Double = {2.2 / 1.01, 1.0 / 3, Math.PI}
            For ctr As Integer = 0 To values.Length - 1
                sw.Write(values(ctr).ToString())
                If ctr <> values.Length - 1 Then sw.Write("|")
            Next
            sw.Close()
    
            Dim restoredValues(values.Length - 1) As Double
            Dim sr As New StreamReader(".\Doubles.dat")
            Dim temp As String = sr.ReadToEnd()
            Dim tempStrings() As String = temp.Split("|"c)
            For ctr As Integer = 0 To tempStrings.Length - 1
                restoredValues(ctr) = Double.Parse(tempStrings(ctr))
            Next
    
            For ctr As Integer = 0 To values.Length - 1
                Console.WriteLine("{0} {2} {1}", values(ctr),
                               restoredValues(ctr),
                               If(values(ctr).Equals(restoredValues(ctr)), "=", "<>"))
            Next
        End Sub
    End Module
    ' The example displays the following output:
    '       2.17821782178218 <> 2.17821782178218
    '       0.333333333333333 <> 0.333333333333333
    '       3.14159265358979 <> 3.14159265358979
    

    Nesse caso, os valores podem ser arredondados com êxito usando a cadeia de caracteres de formato numérico padrão "G17" para preservar a precisão total dos Double valores, como mostra o exemplo a seguir.

    using System;
    using System.IO;
    
    public class Example12
    {
        public static void Main()
        {
            StreamWriter sw = new StreamWriter(@".\Doubles.dat");
            Double[] values = { 2.2 / 1.01, 1.0 / 3, Math.PI };
            for (int ctr = 0; ctr < values.Length; ctr++)
                sw.Write("{0:G17}{1}", values[ctr], ctr < values.Length - 1 ? "|" : "");
    
            sw.Close();
    
            Double[] restoredValues = new Double[values.Length];
            StreamReader sr = new StreamReader(@".\Doubles.dat");
            string temp = sr.ReadToEnd();
            string[] tempStrings = temp.Split('|');
            for (int ctr = 0; ctr < tempStrings.Length; ctr++)
                restoredValues[ctr] = Double.Parse(tempStrings[ctr]);
    
            for (int ctr = 0; ctr < values.Length; ctr++)
                Console.WriteLine("{0} {2} {1}", values[ctr],
                                  restoredValues[ctr],
                                  values[ctr].Equals(restoredValues[ctr]) ? "=" : "<>");
        }
    }
    // The example displays the following output:
    //       2.17821782178218 = 2.17821782178218
    //       0.333333333333333 = 0.333333333333333
    //       3.14159265358979 = 3.14159265358979
    
    open System
    open System.IO
    
    let values = [ 2.2 / 1.01; 1. / 3.; Math.PI ]
    
    using (new StreamWriter(@".\Doubles.dat")) (fun sw -> 
        for i = 0 to values.Length - 1 do
            sw.Write $"""{values[i]:G17}{if i < values.Length - 1 then "|" else ""}""")
    
    using (new StreamReader(@".\Doubles.dat")) (fun sr ->
        let temp = sr.ReadToEnd()
        let tempStrings = temp.Split '|'
        
        let restoredValues = 
          [ for i = 0 to tempStrings.Length - 1 do
                Double.Parse tempStrings[i] ]
    
        for i = 0 to values.Length - 1 do
            printfn $"""{restoredValues[i]} {if values[i].Equals restoredValues[i] then "=" else "<>"} {values[i]}""")
    
    // The example displays the following output:
    //       2.17821782178218 = 2.17821782178218
    //       0.333333333333333 = 0.333333333333333
    //       3.14159265358979 = 3.14159265358979
    
    Imports System.IO
    
    Module Example13
        Public Sub Main()
            Dim sw As New StreamWriter(".\Doubles.dat")
            Dim values() As Double = {2.2 / 1.01, 1.0 / 3, Math.PI}
            For ctr As Integer = 0 To values.Length - 1
                sw.Write("{0:G17}{1}", values(ctr),
                      If(ctr < values.Length - 1, "|", ""))
            Next
            sw.Close()
    
            Dim restoredValues(values.Length - 1) As Double
            Dim sr As New StreamReader(".\Doubles.dat")
            Dim temp As String = sr.ReadToEnd()
            Dim tempStrings() As String = temp.Split("|"c)
            For ctr As Integer = 0 To tempStrings.Length - 1
                restoredValues(ctr) = Double.Parse(tempStrings(ctr))
            Next
    
            For ctr As Integer = 0 To values.Length - 1
                Console.WriteLine("{0} {2} {1}", values(ctr),
                               restoredValues(ctr),
                               If(values(ctr).Equals(restoredValues(ctr)), "=", "<>"))
            Next
        End Sub
    End Module
    ' The example displays the following output:
    '       2.17821782178218 = 2.17821782178218
    '       0.333333333333333 = 0.333333333333333
    '       3.14159265358979 = 3.14159265358979
    

    Importante

    Quando usado com um Double valor, o especificador de formato "R" em alguns casos falha ao realizar com êxito a viagem de ida e volta do valor original. Para garantir que Double os valores sejam bem-sucedidos de ida e volta, use o especificador de formato "G17".

  • 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 Double e a valor Single . 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 no lugar do tipo de Single dados ou use o DoubleRound método para que ambos os valores tenham a mesma precisão.

Além disso, o resultado de operações aritméticas e de atribuição com Double valores pode diferir ligeiramente por plataforma devido à perda de precisão do Double tipo. Por exemplo, o resultado da atribuição de um valor literal Double pode diferir nas versões de 32 bits e 64 bits do .NET. O exemplo a seguir ilustra essa diferença quando o valor literal -4.42330604244772E-305 e uma variável cujo valor é -4.42330604244772E-305 são atribuídos a uma Double variável. Note que o resultado do método, Parse(String) neste caso, não sofre de perda de precisão.

double value = -4.42330604244772E-305;

double fromLiteral = -4.42330604244772E-305;
double fromVariable = value;
double fromParse = Double.Parse("-4.42330604244772E-305");

Console.WriteLine("Double value from literal: {0,29:R}", fromLiteral);
Console.WriteLine("Double value from variable: {0,28:R}", fromVariable);
Console.WriteLine("Double value from Parse method: {0,24:R}", fromParse);
// On 32-bit versions of the .NET Framework, the output is:
//    Double value from literal:        -4.42330604244772E-305
//    Double value from variable:       -4.42330604244772E-305
//    Double value from Parse method:   -4.42330604244772E-305
//
// On other versions of the .NET Framework, the output is:
//    Double value from literal:      -4.4233060424477198E-305
//    Double value from variable:     -4.4233060424477198E-305
//    Double value from Parse method:   -4.42330604244772E-305
let value = -4.42330604244772E-305

let fromLiteral = -4.42330604244772E-305
let fromVariable = value
let fromParse = Double.Parse "-4.42330604244772E-305"

printfn $"Double value from literal: {fromLiteral,29:R}"
printfn $"Double value from variable: {fromVariable,28:R}"
printfn $"Double value from Parse method: {fromParse,24:R}"
// On 32-bit versions of the .NET Framework, the output is:
//    Double value from literal:        -4.42330604244772E-305
//    Double value from variable:       -4.42330604244772E-305
//    Double value from Parse method:   -4.42330604244772E-305
//
// On other versions of the .NET Framework, the output is:
//    Double value from literal:      -4.4233060424477198E-305
//    Double value from variable:     -4.4233060424477198E-305
//    Double value from Parse method:   -4.42330604244772E-305
Dim value As Double = -4.4233060424477198E-305

Dim fromLiteral As Double = -4.4233060424477198E-305
Dim fromVariable As Double = value
Dim fromParse As Double = Double.Parse("-4.42330604244772E-305")

Console.WriteLine("Double value from literal: {0,29:R}", fromLiteral)
Console.WriteLine("Double value from variable: {0,28:R}", fromVariable)
Console.WriteLine("Double value from Parse method: {0,24:R}", fromParse)
' On 32-bit versions of the .NET Framework, the output is:
'    Double value from literal:        -4.42330604244772E-305
'    Double value from variable:       -4.42330604244772E-305
'    Double value from Parse method:   -4.42330604244772E-305
'
' On other versions of the .NET Framework, the output is:
'    Double value from literal:        -4.4233060424477198E-305
'    Double value from variable:       -4.4233060424477198E-305
'    Double value from Parse method:     -4.42330604244772E-305

Teste de igualdade

Para serem considerados iguais, dois Double 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 Double valores, geralmente produzem resultados inesperados. Isso fica evidente no exemplo a seguir, onde dois valores aparentemente iguais Double acabam sendo desiguais porque o primeiro tem 15 dígitos de precisão, enquanto o segundo tem 17.

using System;

public class Example
{
   public static void Main()
   {
      double value1 = .333333333333333;
      double value2 = 1.0/3;
      Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2));
   }
}
// The example displays the following output:
//        0.333333333333333 = 0.33333333333333331: False
open System

let value1 = 0.333333333333333
let value2 = 1. / 3.
printfn $"{value1:R} = {value2:R}: {value1.Equals value2}"
// The example displays the following output:
//        0.333333333333333 = 0.33333333333333331: False
Module Example1
    Public Sub Main()
        Dim value1 As Double = 0.333333333333333
        Dim value2 As Double = 1 / 3
        Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2))
    End Sub
End Module
' The example displays the following output:
'       0.333333333333333 = 0.33333333333333331: 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 Double valor é quadrado e, em seguida, a raiz quadrada é calculada para restaurar o valor original. Um segundo Double é 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(Double) método indica que eles não são iguais. Usar a cadeia de caracteres de formato padrão "R" para retornar uma cadeia de caracteres de resultado que exibe todos os dígitos significativos de cada valor Double mostra que o segundo valor é .0000000000001 menor que o primeiro.

using System;

public class Example1
{
    public static void Main()
    {
        double value1 = 100.10142;
        value1 = Math.Sqrt(Math.Pow(value1, 2));
        double value2 = Math.Pow(value1 * 3.51, 2);
        value2 = Math.Sqrt(value2) / 3.51;
        Console.WriteLine("{0} = {1}: {2}\n",
                          value1, value2, value1.Equals(value2));
        Console.WriteLine("{0:R} = {1:R}", value1, value2);
    }
}
// The example displays the following output:
//    100.10142 = 100.10142: False
//
//    100.10142 = 100.10141999999999
open System

let value1 = 
    Math.Pow(100.10142, 2)
    |> sqrt

let value2 = 
    let v = pown (value1 * 3.51) 2
    (Math.Sqrt v) / 3.51

printfn $"{value1} = {value2}: {value1.Equals value2}\n"
printfn $"{value1:R} = {value2:R}"
// The example displays the following output:
//    100.10142 = 100.10142: False
//
//    100.10142 = 100.10141999999999
Module Example2
    Public Sub Main()
        Dim value1 As Double = 100.10142
        value1 = Math.Sqrt(Math.Pow(value1, 2))
        Dim value2 As Double = Math.Pow(value1 * 3.51, 2)
        value2 = Math.Sqrt(value2) / 3.51
        Console.WriteLine("{0} = {1}: {2}",
                        value1, value2, value1.Equals(value2))
        Console.WriteLine()
        Console.WriteLine("{0:R} = {1:R}", value1, value2)
    End Sub
End Module
' The example displays the following output:
'    100.10142 = 100.10142: False
'    
'    100.10142 = 100.10141999999999

Nos casos em que uma perda de precisão provavelmente afetará o resultado de uma comparação, você pode adotar qualquer uma das seguintes alternativas para 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()
        {
            double value1 = .333333333333333;
            double value2 = 1.0 / 3;
            int precision = 7;
            value1 = Math.Round(value1, precision);
            value2 = 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 v1 = 0.333333333333333
    let v2 = 1. / 3.
    let precision = 7
    let value1 = Math.Round(v1, precision)
    let value2 = Math.Round(v2, precision)
    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 Double = 0.333333333333333
            Dim value2 As Double = 1 / 3
            Dim precision As Integer = 7
            value1 = Math.Round(value1, precision)
            value2 = Math.Round(value2, precision)
            Console.WriteLine("{0: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. Isso 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

    Double.Epsilon às vezes é usado como uma medida absoluta da distância entre dois Double valores ao testar a igualdade. No entanto, Double.Epsilon mede o menor valor possível que pode ser adicionado ou subtraído de um Double cujo valor é zero. Para a maioria dos valores positivos e negativos Double , o valor de Double.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 o IsApproximatelyEqualEquals(Double) método.

    using System;
    
    public class Example3
    {
        public static void Main()
        {
            double one1 = .1 * 10;
            double one2 = 0;
            for (int ctr = 1; ctr <= 10; ctr++)
                one2 += .1;
    
            Console.WriteLine("{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, .000000001));
        }
    
        static bool IsApproximatelyEqual(double value1, double value2, double epsilon)
        {
            // If they are equal anyway, just return True.
            if (value1.Equals(value2))
                return true;
    
            // Handle NaN, Infinity.
            if (Double.IsInfinity(value1) | Double.IsNaN(value1))
                return value1.Equals(value2);
            else if (Double.IsInfinity(value2) | Double.IsNaN(value2))
                return value1.Equals(value2);
    
            // Handle zero to avoid division by zero
            double divisor = Math.Max(value1, value2);
            if (divisor.Equals(0))
                divisor = Math.Min(value1, value2);
    
            return Math.Abs((value1 - value2) / divisor) <= epsilon;
        }
    }
    // The example displays the following output:
    //       1 = 0.99999999999999989: False
    //       1 is approximately equal to 0.99999999999999989: True
    
    open System
    
    let isApproximatelyEqual (value1: double) (value2: double) (epsilon: double) =
        // If they are equal anyway, just return True.
        if value1.Equals value2 then 
            true
        else
            // Handle NaN, Infinity.
            if Double.IsInfinity value1 || Double.IsNaN value1 then 
                value1.Equals value2
            elif Double.IsInfinity value2 || Double.IsNaN value2 then
                value1.Equals value2
            else
                // Handle zero to avoid division by zero
                let divisor = max value1 value2
                let divisor = 
                    if divisor.Equals 0 then
                        min value1 value2
                    else 
                        divisor
                abs ((value1 - value2) / divisor) <= epsilon
    
    let one1 = 0.1 * 10.
    let mutable one2 = 0.
    for _ = 1 to 10 do
        one2 <- one2 + 0.1
    
    printfn $"{one1:R} = {one2:R}: {one1.Equals one2}"
    printfn $"{one1:R} is approximately equal to {one2:R}: {isApproximatelyEqual one1 one2 0.000000001}"
    
    // The example displays the following output:
    //       1 = 0.99999999999999989: False
    //       1 is approximately equal to 0.99999999999999989: True
    
    Module Example4
        Public Sub Main()
            Dim one1 As Double = 0.1 * 10
            Dim one2 As Double = 0
            For ctr As Integer = 1 To 10
                one2 += 0.1
            Next
            Console.WriteLine("{0: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.000000001))
        End Sub
    
        Function IsApproximatelyEqual(value1 As Double, value2 As Double,
                                     epsilon As Double) As Boolean
            ' If they are equal anyway, just return True.
            If value1.Equals(value2) Then Return True
    
            ' Handle NaN, Infinity.
            If Double.IsInfinity(value1) Or Double.IsNaN(value1) Then
                Return value1.Equals(value2)
            ElseIf Double.IsInfinity(value2) Or Double.IsNaN(value2) Then
                Return value1.Equals(value2)
            End If
    
            ' Handle zero to avoid division by zero
            Dim divisor As Double = Math.Max(value1, value2)
            If divisor.Equals(0) Then
                divisor = Math.Min(value1, value2)
            End If
    
            Return Math.Abs((value1 - value2) / divisor) <= epsilon
        End Function
    End Module
    ' The example displays the following output:
    '       1 = 0.99999999999999989: False
    '       1 is approximately equal to 0.99999999999999989: True
    

Valores de ponto flutuante e exceções

Ao contrário das operações com tipos integrais, que lançam exceções em casos de estouro ou operações ilegais, como divisão por zero, as operações com valores de ponto flutuante não lançam exceções. Em vez disso, em situações excepcionais, 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 muito pequenos são multiplicados, como mostra o exemplo a seguir.

    using System;
    
    public class Example6
    {
        public static void Main()
        {
            Double value1 = 1.1632875981534209e-225;
            Double value2 = 9.1642346778e-175;
            Double result = value1 * value2;
            Console.WriteLine("{0} * {1} = {2}", value1, value2, result);
            Console.WriteLine("{0} = 0: {1}", result, result.Equals(0.0));
        }
    }
    // The example displays the following output:
    //       1.16328759815342E-225 * 9.1642346778E-175 = 0
    //       0 = 0: True
    
    let value1 = 1.1632875981534209e-225
    let value2 = 9.1642346778e-175
    let result = value1 * value2
    printfn $"{value1} * {value2} = {result}"
    printfn $"{result} = 0: {result.Equals 0.0}"
    // The example displays the following output:
    //       1.16328759815342E-225 * 9.1642346778E-175 = 0
    //       0 = 0: True
    
    Module Example7
        Public Sub Main()
            Dim value1 As Double = 1.1632875981534209E-225
            Dim value2 As Double = 9.1642346778E-175
            Dim result As Double = value1 * value2
            Console.WriteLine("{0} * {1} = {2}", value1, value2, result)
            Console.WriteLine("{0} = 0: {1}", result, result.Equals(0.0))
        End Sub
    End Module
    ' The example displays the following output:
    '       1.16328759815342E-225 * 9.1642346778E-175 = 0
    '       0 = 0: 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 Double.MaxValueDouble.MinValue é PositiveInfinityNegativeInfinity, como mostra o exemplo a seguir.

    using System;
    
    public class Example7
    {
        public static void Main()
        {
            Double value1 = 4.565e153;
            Double value2 = 6.9375e172;
            Double result = value1 * value2;
            Console.WriteLine("PositiveInfinity: {0}",
                               Double.IsPositiveInfinity(result));
            Console.WriteLine("NegativeInfinity: {0}\n",
                              Double.IsNegativeInfinity(result));
    
            value1 = -value1;
            result = value1 * value2;
            Console.WriteLine("PositiveInfinity: {0}",
                               Double.IsPositiveInfinity(result));
            Console.WriteLine("NegativeInfinity: {0}",
                              Double.IsNegativeInfinity(result));
        }
    }
    
    // The example displays the following output:
    //       PositiveInfinity: True
    //       NegativeInfinity: False
    //
    //       PositiveInfinity: False
    //       NegativeInfinity: True
    
    open System
    
    let value1 = 4.565e153
    let value2 = 6.9375e172
    let result = value1 * value2
    printfn $"PositiveInfinity: {Double.IsPositiveInfinity result}"
    printfn $"NegativeInfinity: {Double.IsNegativeInfinity result}\n"
    
    let value3 = - value1
    let result2 = value2 * value3
    printfn $"PositiveInfinity: {Double.IsPositiveInfinity result2}"
    printfn $"NegativeInfinity: {Double.IsNegativeInfinity result2}"
    
    // The example displays the following output:
    //       PositiveInfinity: True
    //       NegativeInfinity: False
    //
    //       PositiveInfinity: False
    //       NegativeInfinity: True
    
    Module Example8
        Public Sub Main()
            Dim value1 As Double = 4.565E+153
            Dim value2 As Double = 6.9375E+172
            Dim result As Double = value1 * value2
            Console.WriteLine("PositiveInfinity: {0}",
                             Double.IsPositiveInfinity(result))
            Console.WriteLine("NegativeInfinity: {0}",
                            Double.IsNegativeInfinity(result))
            Console.WriteLine()
            value1 = -value1
            result = value1 * value2
            Console.WriteLine("PositiveInfinity: {0}",
                             Double.IsPositiveInfinity(result))
            Console.WriteLine("NegativeInfinity: {0}",
                            Double.IsNegativeInfinity(result))
        End Sub
    End Module
    ' The example displays the following output:
    '       PositiveInfinity: True
    '       NegativeInfinity: False
    '       
    '       PositiveInfinity: False
    '       NegativeInfinity: 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 uma entrada inválida. Por exemplo, chamar o método com um valor negativo retorna NaN, assim como chamar o Math.AcosMath.Sqrt método com um valor maior que um ou menor que um negativo.

    • Qualquer operação com um argumento cujo valor seja Double.NaN.

Conversões de tipo

A Double 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 conversão do valor de qualquer tipo numérico primitivo em a é uma conversão de ampliação e, portanto, não requer um operador de conversão explícito ou chamada para um método de conversão, a Double menos que um compilador exija isso explicitamente. Por exemplo, o compilador C# requer um operador de conversão para conversões de Decimal para Double, enquanto o compilador do Visual Basic não. O exemplo a seguir converte o valor mínimo ou máximo de outros tipos numéricos primitivos em um Doublearquivo .

using System;

public class Example4
{
    public static void Main()
    {
        dynamic[] values = { Byte.MinValue, Byte.MaxValue, Decimal.MinValue,
                           Decimal.MaxValue, Int16.MinValue, Int16.MaxValue,
                           Int32.MinValue, Int32.MaxValue, Int64.MinValue,
                           Int64.MaxValue, SByte.MinValue, SByte.MaxValue,
                           Single.MinValue, Single.MaxValue, UInt16.MinValue,
                           UInt16.MaxValue, UInt32.MinValue, UInt32.MaxValue,
                           UInt64.MinValue, UInt64.MaxValue };
        double dblValue;
        foreach (var value in values)
        {
            if (value.GetType() == typeof(Decimal))
                dblValue = (Double)value;
            else
                dblValue = value;
            Console.WriteLine("{0} ({1}) --> {2:R} ({3})",
                              value, value.GetType().Name,
                              dblValue, dblValue.GetType().Name);
        }
    }
}
// The example displays the following output:
//    0 (Byte) --> 0 (Double)
//    255 (Byte) --> 255 (Double)
//    -79228162514264337593543950335 (Decimal) --> -7.9228162514264338E+28 (Double)
//    79228162514264337593543950335 (Decimal) --> 7.9228162514264338E+28 (Double)
//    -32768 (Int16) --> -32768 (Double)
//    32767 (Int16) --> 32767 (Double)
//    -2147483648 (Int32) --> -2147483648 (Double)
//    2147483647 (Int32) --> 2147483647 (Double)
//    -9223372036854775808 (Int64) --> -9.2233720368547758E+18 (Double)
//    9223372036854775807 (Int64) --> 9.2233720368547758E+18 (Double)
//    -128 (SByte) --> -128 (Double)
//    127 (SByte) --> 127 (Double)
//    -3.402823E+38 (Single) --> -3.4028234663852886E+38 (Double)
//    3.402823E+38 (Single) --> 3.4028234663852886E+38 (Double)
//    0 (UInt16) --> 0 (Double)
//    65535 (UInt16) --> 65535 (Double)
//    0 (UInt32) --> 0 (Double)
//    4294967295 (UInt32) --> 4294967295 (Double)
//    0 (UInt64) --> 0 (Double)
//    18446744073709551615 (UInt64) --> 1.8446744073709552E+19 (Double)
open System

let values: obj[] = 
    [| Byte.MinValue; Byte.MaxValue; Decimal.MinValue
       Decimal.MaxValue; Int16.MinValue; Int16.MaxValue
       Int32.MinValue; Int32.MaxValue; Int64.MinValue
       Int64.MaxValue; SByte.MinValue; SByte.MaxValue
       Single.MinValue; Single.MaxValue; UInt16.MinValue
       UInt16.MaxValue; UInt32.MinValue, UInt32.MaxValue
       UInt64.MinValue; UInt64.MaxValue |]

for value in values do
    let dblValue = value :?> double
    printfn $"{value} ({value.GetType().Name}) --> {dblValue:R} ({dblValue.GetType().Name})"
// The example displays the following output:
//    0 (Byte) --> 0 (Double)
//    255 (Byte) --> 255 (Double)
//    -79228162514264337593543950335 (Decimal) --> -7.9228162514264338E+28 (Double)
//    79228162514264337593543950335 (Decimal) --> 7.9228162514264338E+28 (Double)
//    -32768 (Int16) --> -32768 (Double)
//    32767 (Int16) --> 32767 (Double)
//    -2147483648 (Int32) --> -2147483648 (Double)
//    2147483647 (Int32) --> 2147483647 (Double)
//    -9223372036854775808 (Int64) --> -9.2233720368547758E+18 (Double)
//    9223372036854775807 (Int64) --> 9.2233720368547758E+18 (Double)
//    -128 (SByte) --> -128 (Double)
//    127 (SByte) --> 127 (Double)
//    -3.402823E+38 (Single) --> -3.4028234663852886E+38 (Double)
//    3.402823E+38 (Single) --> 3.4028234663852886E+38 (Double)
//    0 (UInt16) --> 0 (Double)
//    65535 (UInt16) --> 65535 (Double)
//    0 (UInt32) --> 0 (Double)
//    4294967295 (UInt32) --> 4294967295 (Double)
//    0 (UInt64) --> 0 (Double)
//    18446744073709551615 (UInt64) --> 1.8446744073709552E+19 (Double)
Module Example5
    Public Sub Main()
        Dim values() As Object = {Byte.MinValue, Byte.MaxValue, Decimal.MinValue,
                                 Decimal.MaxValue, Int16.MinValue, Int16.MaxValue,
                                 Int32.MinValue, Int32.MaxValue, Int64.MinValue,
                                 Int64.MaxValue, SByte.MinValue, SByte.MaxValue,
                                 Single.MinValue, Single.MaxValue, UInt16.MinValue,
                                 UInt16.MaxValue, UInt32.MinValue, UInt32.MaxValue,
                                 UInt64.MinValue, UInt64.MaxValue}
        Dim dblValue As Double
        For Each value In values
            dblValue = value
            Console.WriteLine("{0} ({1}) --> {2:R} ({3})",
                           value, value.GetType().Name,
                           dblValue, dblValue.GetType().Name)
        Next
    End Sub
End Module
' The example displays the following output:
'    0 (Byte) --> 0 (Double)
'    255 (Byte) --> 255 (Double)
'    -79228162514264337593543950335 (Decimal) --> -7.9228162514264338E+28 (Double)
'    79228162514264337593543950335 (Decimal) --> 7.9228162514264338E+28 (Double)
'    -32768 (Int16) --> -32768 (Double)
'    32767 (Int16) --> 32767 (Double)
'    -2147483648 (Int32) --> -2147483648 (Double)
'    2147483647 (Int32) --> 2147483647 (Double)
'    -9223372036854775808 (Int64) --> -9.2233720368547758E+18 (Double)
'    9223372036854775807 (Int64) --> 9.2233720368547758E+18 (Double)
'    -128 (SByte) --> -128 (Double)
'    127 (SByte) --> 127 (Double)
'    -3.402823E+38 (Single) --> -3.4028234663852886E+38 (Double)
'    3.402823E+38 (Single) --> 3.4028234663852886E+38 (Double)
'    0 (UInt16) --> 0 (Double)
'    65535 (UInt16) --> 65535 (Double)
'    0 (UInt32) --> 0 (Double)
'    4294967295 (UInt32) --> 4294967295 (Double)
'    0 (UInt64) --> 0 (Double)
'    18446744073709551615 (UInt64) --> 1.8446744073709552E+19 (Double)

Além disso, os Single valores Single.NaN, e convertem em Double.NaN, e Single.NegativeInfinityDouble.NegativeInfinity, Single.PositiveInfinityDouble.PositiveInfinityrespectivamente.

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

A conversão de um valor em um valor de qualquer outro tipo de dados numérico primitivo é uma conversão de estreitamento e requer um operador de conversão (em C#), um método de conversão (no Visual Basic) ou uma chamada para um DoubleConvert método. 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 exceção OverflowException.
Single Single.NegativeInfinity para valores negativos.

Single.PositiveInfinity para valores positivos.

Além disso, , e Double.NegativeInfinity lançar um para conversões em inteiros em um contexto verificado, Double.NaNDouble.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 SingleSingle.NaN, e Single.NegativeInfinity, Single.PositiveInfinityrespectivamente.

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

O exemplo a seguir converte vários Double 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 ao módulo Verificado). 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+checkedopen Checked instrução.

using System;

public class Example5
{
    public static void Main()
    {
        Double[] values = { Double.MinValue, -67890.1234, -12345.6789,
                          12345.6789, 67890.1234, Double.MaxValue,
                          Double.NaN, Double.PositiveInfinity,
                          Double.NegativeInfinity };
        checked
        {
            foreach (var value in values)
            {
                try
                {
                    Int64 lValue = (long)value;
                    Console.WriteLine("{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);
                }
                try
                {
                    Single sValue = (float)value;
                    Console.WriteLine("{0} ({1}) --> {2} ({3})",
                                      value, value.GetType().Name,
                                      sValue, sValue.GetType().Name);
                }
                catch (OverflowException)
                {
                    Console.WriteLine("Unable to convert {0} to Single.", value);
                }
                Console.WriteLine();
            }
        }
    }
}
// The example displays the following output for conversions performed
// in a checked context:
//       Unable to convert -1.79769313486232E+308 to Int64.
//       Unable to convert -1.79769313486232E+308 to UInt64.
//       Unable to convert -1.79769313486232E+308 to Decimal.
//       -1.79769313486232E+308 (Double) --> -Infinity (Single)
//
//       -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
//       Unable to convert -67890.1234 to UInt64.
//       -67890.1234 (Double) --> -67890.1234 (Decimal)
//       -67890.1234 (Double) --> -67890.13 (Single)
//
//       -12345.6789 (Double) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
//       Unable to convert -12345.6789 to UInt64.
//       -12345.6789 (Double) --> -12345.6789 (Decimal)
//       -12345.6789 (Double) --> -12345.68 (Single)
//
//       12345.6789 (Double) --> 12345 (0x0000000000003039) (Int64)
//       12345.6789 (Double) --> 12345 (0x0000000000003039) (UInt64)
//       12345.6789 (Double) --> 12345.6789 (Decimal)
//       12345.6789 (Double) --> 12345.68 (Single)
//
//       67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
//       67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
//       67890.1234 (Double) --> 67890.1234 (Decimal)
//       67890.1234 (Double) --> 67890.13 (Single)
//
//       Unable to convert 1.79769313486232E+308 to Int64.
//       Unable to convert 1.79769313486232E+308 to UInt64.
//       Unable to convert 1.79769313486232E+308 to Decimal.
//       1.79769313486232E+308 (Double) --> Infinity (Single)
//
//       Unable to convert NaN to Int64.
//       Unable to convert NaN to UInt64.
//       Unable to convert NaN to Decimal.
//       NaN (Double) --> NaN (Single)
//
//       Unable to convert Infinity to Int64.
//       Unable to convert Infinity to UInt64.
//       Unable to convert Infinity to Decimal.
//       Infinity (Double) --> Infinity (Single)
//
//       Unable to convert -Infinity to Int64.
//       Unable to convert -Infinity to UInt64.
//       Unable to convert -Infinity to Decimal.
//       -Infinity (Double) --> -Infinity (Single)
// The example displays the following output for conversions performed
// in an unchecked context:
//       -1.79769313486232E+308 (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       -1.79769313486232E+308 (Double) --> 9223372036854775808 (0x8000000000000000) (UInt64)
//       Unable to convert -1.79769313486232E+308 to Decimal.
//       -1.79769313486232E+308 (Double) --> -Infinity (Single)
//
//       -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
//       -67890.1234 (Double) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE) (UInt64)
//       -67890.1234 (Double) --> -67890.1234 (Decimal)
//       -67890.1234 (Double) --> -67890.13 (Single)
//
//       -12345.6789 (Double) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
//       -12345.6789 (Double) --> 18446744073709539271 (0xFFFFFFFFFFFFCFC7) (UInt64)
//       -12345.6789 (Double) --> -12345.6789 (Decimal)
//       -12345.6789 (Double) --> -12345.68 (Single)
//
//       12345.6789 (Double) --> 12345 (0x0000000000003039) (Int64)
//       12345.6789 (Double) --> 12345 (0x0000000000003039) (UInt64)
//       12345.6789 (Double) --> 12345.6789 (Decimal)
//       12345.6789 (Double) --> 12345.68 (Single)
//
//       67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
//       67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
//       67890.1234 (Double) --> 67890.1234 (Decimal)
//       67890.1234 (Double) --> 67890.13 (Single)
//
//       1.79769313486232E+308 (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       1.79769313486232E+308 (Double) --> 0 (0x0000000000000000) (UInt64)
//       Unable to convert 1.79769313486232E+308 to Decimal.
//       1.79769313486232E+308 (Double) --> Infinity (Single)
//
//       NaN (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       NaN (Double) --> 0 (0x0000000000000000) (UInt64)
//       Unable to convert NaN to Decimal.
//       NaN (Double) --> NaN (Single)
//
//       Infinity (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       Infinity (Double) --> 0 (0x0000000000000000) (UInt64)
//       Unable to convert Infinity to Decimal.
//       Infinity (Double) --> Infinity (Single)
//
//       -Infinity (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       -Infinity (Double) --> 9223372036854775808 (0x8000000000000000) (UInt64)
//       Unable to convert -Infinity to Decimal.
//       -Infinity (Double) --> -Infinity (Single)
open System
open Checked

let values = 
    [| Double.MinValue; -67890.1234; -12345.6789
       12345.6789; 67890.1234; Double.MaxValue
       Double.NaN; Double.PositiveInfinity;
       Double.NegativeInfinity |]

for value in values do
    try
        let lValue = int64 value
        printfn $"{value} ({value.GetType().Name}) --> {lValue} (0x{lValue:X16}) ({lValue.GetType().Name})"
    with :? OverflowException ->
        printfn $"Unable to convert {value} to Int64."
    try
        let ulValue = uint64 value
        printfn $"{value} ({value.GetType().Name}) --> {ulValue} (0x{ulValue:X16}) ({ulValue.GetType().Name})"
    with :? OverflowException ->
        printfn $"Unable to convert {value} to UInt64."
    try
        let dValue = decimal value
        printfn $"{value} ({value.GetType().Name}) --> {dValue} ({dValue.GetType().Name})"
    with :? OverflowException ->
        printfn $"Unable to convert {value} to Decimal."
    try
        let sValue = float32 value
        printfn $"{value} ({value.GetType().Name}) --> {sValue} ({sValue.GetType().Name})"
    with :? OverflowException ->
        printfn $"Unable to convert {value} to Single."
    printfn ""
// The example displays the following output for conversions performed
// in a checked context:
//       Unable to convert -1.79769313486232E+308 to Int64.
//       Unable to convert -1.79769313486232E+308 to UInt64.
//       Unable to convert -1.79769313486232E+308 to Decimal.
//       -1.79769313486232E+308 (Double) --> -Infinity (Single)
//
//       -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
//       Unable to convert -67890.1234 to UInt64.
//       -67890.1234 (Double) --> -67890.1234 (Decimal)
//       -67890.1234 (Double) --> -67890.13 (Single)
//
//       -12345.6789 (Double) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
//       Unable to convert -12345.6789 to UInt64.
//       -12345.6789 (Double) --> -12345.6789 (Decimal)
//       -12345.6789 (Double) --> -12345.68 (Single)
//
//       12345.6789 (Double) --> 12345 (0x0000000000003039) (Int64)
//       12345.6789 (Double) --> 12345 (0x0000000000003039) (UInt64)
//       12345.6789 (Double) --> 12345.6789 (Decimal)
//       12345.6789 (Double) --> 12345.68 (Single)
//
//       67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
//       67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
//       67890.1234 (Double) --> 67890.1234 (Decimal)
//       67890.1234 (Double) --> 67890.13 (Single)
//
//       Unable to convert 1.79769313486232E+308 to Int64.
//       Unable to convert 1.79769313486232E+308 to UInt64.
//       Unable to convert 1.79769313486232E+308 to Decimal.
//       1.79769313486232E+308 (Double) --> Infinity (Single)
//
//       Unable to convert NaN to Int64.
//       Unable to convert NaN to UInt64.
//       Unable to convert NaN to Decimal.
//       NaN (Double) --> NaN (Single)
//
//       Unable to convert Infinity to Int64.
//       Unable to convert Infinity to UInt64.
//       Unable to convert Infinity to Decimal.
//       Infinity (Double) --> Infinity (Single)
//
//       Unable to convert -Infinity to Int64.
//       Unable to convert -Infinity to UInt64.
//       Unable to convert -Infinity to Decimal.
//       -Infinity (Double) --> -Infinity (Single)
// The example displays the following output for conversions performed
// in an unchecked context:
//       -1.79769313486232E+308 (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       -1.79769313486232E+308 (Double) --> 9223372036854775808 (0x8000000000000000) (UInt64)
//       Unable to convert -1.79769313486232E+308 to Decimal.
//       -1.79769313486232E+308 (Double) --> -Infinity (Single)
//
//       -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
//       -67890.1234 (Double) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE) (UInt64)
//       -67890.1234 (Double) --> -67890.1234 (Decimal)
//       -67890.1234 (Double) --> -67890.13 (Single)
//
//       -12345.6789 (Double) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
//       -12345.6789 (Double) --> 18446744073709539271 (0xFFFFFFFFFFFFCFC7) (UInt64)
//       -12345.6789 (Double) --> -12345.6789 (Decimal)
//       -12345.6789 (Double) --> -12345.68 (Single)
//
//       12345.6789 (Double) --> 12345 (0x0000000000003039) (Int64)
//       12345.6789 (Double) --> 12345 (0x0000000000003039) (UInt64)
//       12345.6789 (Double) --> 12345.6789 (Decimal)
//       12345.6789 (Double) --> 12345.68 (Single)
//
//       67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
//       67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
//       67890.1234 (Double) --> 67890.1234 (Decimal)
//       67890.1234 (Double) --> 67890.13 (Single)
//
//       1.79769313486232E+308 (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       1.79769313486232E+308 (Double) --> 0 (0x0000000000000000) (UInt64)
//       Unable to convert 1.79769313486232E+308 to Decimal.
//       1.79769313486232E+308 (Double) --> Infinity (Single)
//
//       NaN (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       NaN (Double) --> 0 (0x0000000000000000) (UInt64)
//       Unable to convert NaN to Decimal.
//       NaN (Double) --> NaN (Single)
//
//       Infinity (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       Infinity (Double) --> 0 (0x0000000000000000) (UInt64)
//       Unable to convert Infinity to Decimal.
//       Infinity (Double) --> Infinity (Single)
//
//       -Infinity (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       -Infinity (Double) --> 9223372036854775808 (0x8000000000000000) (UInt64)
//       Unable to convert -Infinity to Decimal.
//       -Infinity (Double) --> -Infinity (Single)
Module Example6
    Public Sub Main()
        Dim values() As Double = {Double.MinValue, -67890.1234, -12345.6789,
                                 12345.6789, 67890.1234, Double.MaxValue,
                                 Double.NaN, Double.PositiveInfinity,
                                 Double.NegativeInfinity}
        For Each value In values
            Try
                Dim lValue As Int64 = CLng(value)
                Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
                               value, value.GetType().Name,
                               lValue, lValue.GetType().Name)
            Catch e As OverflowException
                Console.WriteLine("Unable to convert {0} to Int64.", value)
            End Try
            Try
                Dim ulValue As UInt64 = CULng(value)
                Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
                               value, value.GetType().Name,
                               ulValue, ulValue.GetType().Name)
            Catch e As OverflowException
                Console.WriteLine("Unable to convert {0} to UInt64.", value)
            End Try
            Try
                Dim dValue As Decimal = CDec(value)
                Console.WriteLine("{0} ({1}) --> {2} ({3})",
                               value, value.GetType().Name,
                               dValue, dValue.GetType().Name)
            Catch e As OverflowException
                Console.WriteLine("Unable to convert {0} to Decimal.", value)
            End Try
            Try
                Dim sValue As Single = CSng(value)
                Console.WriteLine("{0} ({1}) --> {2} ({3})",
                               value, value.GetType().Name,
                               sValue, sValue.GetType().Name)
            Catch e As OverflowException
                Console.WriteLine("Unable to convert {0} to Single.", value)
            End Try
            Console.WriteLine()
        Next
    End Sub
End Module
' The example displays the following output for conversions performed
' in a checked context:
'       Unable to convert -1.79769313486232E+308 to Int64.
'       Unable to convert -1.79769313486232E+308 to UInt64.
'       Unable to convert -1.79769313486232E+308 to Decimal.
'       -1.79769313486232E+308 (Double) --> -Infinity (Single)
'
'       -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
'       Unable to convert -67890.1234 to UInt64.
'       -67890.1234 (Double) --> -67890.1234 (Decimal)
'       -67890.1234 (Double) --> -67890.13 (Single)
'
'       -12345.6789 (Double) --> -12346 (0xFFFFFFFFFFFFCFC6) (Int64)
'       Unable to convert -12345.6789 to UInt64.
'       -12345.6789 (Double) --> -12345.6789 (Decimal)
'       -12345.6789 (Double) --> -12345.68 (Single)
'
'       12345.6789 (Double) --> 12346 (0x000000000000303A) (Int64)
'       12345.6789 (Double) --> 12346 (0x000000000000303A) (UInt64)
'       12345.6789 (Double) --> 12345.6789 (Decimal)
'       12345.6789 (Double) --> 12345.68 (Single)
'
'       67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
'       67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
'       67890.1234 (Double) --> 67890.1234 (Decimal)
'       67890.1234 (Double) --> 67890.13 (Single)
'
'       Unable to convert 1.79769313486232E+308 to Int64.
'       Unable to convert 1.79769313486232E+308 to UInt64.
'       Unable to convert 1.79769313486232E+308 to Decimal.
'       1.79769313486232E+308 (Double) --> Infinity (Single)
'
'       Unable to convert NaN to Int64.
'       Unable to convert NaN to UInt64.
'       Unable to convert NaN to Decimal.
'       NaN (Double) --> NaN (Single)
'
'       Unable to convert Infinity to Int64.
'       Unable to convert Infinity to UInt64.
'       Unable to convert Infinity to Decimal.
'       Infinity (Double) --> Infinity (Single)
'
'       Unable to convert -Infinity to Int64.
'       Unable to convert -Infinity to UInt64.
'       Unable to convert -Infinity to Decimal.
'       -Infinity (Double) --> -Infinity (Single)
' The example displays the following output for conversions performed
' in an unchecked context:
'       -1.79769313486232E+308 (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
'       -1.79769313486232E+308 (Double) --> 9223372036854775808 (0x8000000000000000) (UInt64)
'       Unable to convert -1.79769313486232E+308 to Decimal.
'       -1.79769313486232E+308 (Double) --> -Infinity (Single)
'
'       -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
'       -67890.1234 (Double) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE) (UInt64)
'       -67890.1234 (Double) --> -67890.1234 (Decimal)
'       -67890.1234 (Double) --> -67890.13 (Single)
'
'       -12345.6789 (Double) --> -12346 (0xFFFFFFFFFFFFCFC6) (Int64)
'       -12345.6789 (Double) --> 18446744073709539270 (0xFFFFFFFFFFFFCFC6) (UInt64)
'       -12345.6789 (Double) --> -12345.6789 (Decimal)
'       -12345.6789 (Double) --> -12345.68 (Single)
'
'       12345.6789 (Double) --> 12346 (0x000000000000303A) (Int64)
'       12345.6789 (Double) --> 12346 (0x000000000000303A) (UInt64)
'       12345.6789 (Double) --> 12345.6789 (Decimal)
'       12345.6789 (Double) --> 12345.68 (Single)
'
'       67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
'       67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
'       67890.1234 (Double) --> 67890.1234 (Decimal)
'       67890.1234 (Double) --> 67890.13 (Single)
'
'       1.79769313486232E+308 (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
'       1.79769313486232E+308 (Double) --> 0 (0x0000000000000000) (UInt64)
'       Unable to convert 1.79769313486232E+308 to Decimal.
'       1.79769313486232E+308 (Double) --> Infinity (Single)
'
'       NaN (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
'       NaN (Double) --> 0 (0x0000000000000000) (UInt64)
'       Unable to convert NaN to Decimal.
'       NaN (Double) --> NaN (Single)
'
'       Infinity (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
'       Infinity (Double) --> 0 (0x0000000000000000) (UInt64)
'       Unable to convert Infinity to Decimal.
'       Infinity (Double) --> Infinity (Single)
'
'       -Infinity (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
'       -Infinity (Double) --> 9223372036854775808 (0x8000000000000000) (UInt64)
'       Unable to convert -Infinity to Decimal.
'       -Infinity (Double) --> -Infinity (Single)

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 Double estrutura e os tipos relacionados fornecem métodos para executar operações nas seguintes áreas:

  • 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 Double valores.

    A Double 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. Se um dos operandos for um tipo numérico diferente de um , ele será convertido em um DoubleDouble antes de realizar a comparação.

    Aviso

    Devido a diferenças na precisão, dois Double 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 Double 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 por Double métodos. Se um dos operandos em uma operação matemática for um tipo numérico diferente de um , ele será convertido em um DoubleDouble antes de executar a operação. O resultado da operação também é um Double valor.

    Outras operações matemáticas podem ser executadas chamando static (Shared no Visual Basic) métodos na System.Math classe. Ele inclui 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

    Você também pode manipular os bits individuais em um Double valor. O BitConverter.DoubleToInt64Bits método preserva o padrão de bits de um valor em um Double inteiro de 64 bits. O BitConverter.GetBytes(Double) método retorna seu padrão de bits em uma matriz de bytes.

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

  • Formatação. Você pode converter um Double valor em sua representação de cadeia de caracteres chamando o método ou usando o ToString 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 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 Double valor chamando o Parse método ou 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 Double 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 de todos os outros tipos numéricos padrão em Double valores. A conversão de um valor de qualquer tipo numérico padrão em um é uma conversão de alargamento e não requer o usuário de um Double operador de fundição ou método de conversão,

    No entanto, a conversão de e Single valores pode envolver uma perda de Int64 precisão. A tabela a seguir lista as diferenças de precisão para cada um desses tipos:

    Tipo Máxima precisão Precisão interna
    Double 15 17
    Int64 19 dígitos decimais 19 dígitos decimais
    Single 7 dígitos decimais 9 dígitos decimais

    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 convertido em um Doublearquivo .

    using System;
    
    public class Example13
    {
        public static void Main()
        {
            Double value = .1;
            Double result1 = value * 10;
            Double result2 = 0;
            for (int ctr = 1; ctr <= 10; ctr++)
                result2 += value;
    
            Console.WriteLine(".1 * 10:           {0:R}", result1);
            Console.WriteLine(".1 Added 10 times: {0:R}", result2);
        }
    }
    // The example displays the following output:
    //       .1 * 10:           1
    //       .1 Added 10 times: 0.99999999999999989
    
    let value = 0.1
    let result1 = value * 10.
    let mutable result2 = 0.
    for i = 1 to 10 do
        result2 <- result2 + value
    
    printfn $".1 * 10:           {result1:R}"
    printfn $".1 Added 10 times: {result2:R}"
    // The example displays the following output:
    //       .1 * 10:           1
    //       .1 Added 10 times: 0.99999999999999989
    
    Module Example14
        Public Sub Main()
            Dim value As Double = 0.1
            Dim result1 As Double = value * 10
            Dim result2 As Double
            For ctr As Integer = 1 To 10
                result2 += value
            Next
            Console.WriteLine(".1 * 10:           {0:R}", result1)
            Console.WriteLine(".1 Added 10 times: {0:R}", result2)
        End Sub
    End Module
    ' The example displays the following output:
    '       .1 * 10:           1
    '       .1 Added 10 times: 0.99999999999999989
    

Exemplos

O exemplo de código a seguir ilustra o uso de Double:

// The Temperature class stores the temperature as a Double
// and delegates most of the functionality to the Double
// implementation.
public class Temperature : IComparable, IFormattable
{
    // IComparable.CompareTo implementation.
    public int CompareTo(object obj) {
        if (obj == null) return 1;

        Temperature temp = obj as Temperature;
        if (obj != null)
            return m_value.CompareTo(temp.m_value);
        else
            throw new ArgumentException("object is not a Temperature");	
    }

    // IFormattable.ToString implementation.
    public string ToString(string format, IFormatProvider provider) {
        if( format != null ) {
            if( format.Equals("F") ) {
                return String.Format("{0}'F", this.Value.ToString());
            }
            if( format.Equals("C") ) {
                return String.Format("{0}'C", this.Celsius.ToString());
            }
        }

        return m_value.ToString(format, provider);
    }

    // Parses the temperature from a string in the form
    // [ws][sign]digits['F|'C][ws]
    public static Temperature Parse(string s, NumberStyles styles, IFormatProvider provider) {
        Temperature temp = new Temperature();

        if( s.TrimEnd(null).EndsWith("'F") ) {
            temp.Value = Double.Parse( s.Remove(s.LastIndexOf('\''), 2), styles, provider);
        }
        else if( s.TrimEnd(null).EndsWith("'C") ) {
            temp.Celsius = Double.Parse( s.Remove(s.LastIndexOf('\''), 2), styles, provider);
        }
        else {
            temp.Value = Double.Parse(s, styles, provider);
        }

        return temp;
    }

    // The value holder
    protected double m_value;

    public double Value {
        get {
            return m_value;
        }
        set {
            m_value = value;
        }
    }

    public double Celsius {
        get {
            return (m_value-32.0)/1.8;
        }
        set {
            m_value = 1.8*value+32.0;
        }
    }
}
// The Temperature class stores the temperature as a Double
// and delegates most of the functionality to the Double
// implementation.
type Temperature() =
    member val Value = 0. with get, set

    member this.Celsius
        with get () = (this.Value - 32.) / 1.8
        and set (value) =
            this.Value <- 1.8 * value + 32.

    // Parses the temperature from a string in the form
    // [ws][sign]digits['F|'C][ws]
    static member Parse(s: string, styles: NumberStyles, provider: IFormatProvider) =
        let temp = Temperature()

        if s.TrimEnd(null).EndsWith "'F" then
            temp.Value <- Double.Parse(s.Remove(s.LastIndexOf '\'', 2), styles, provider)
        elif s.TrimEnd(null).EndsWith "'C" then
            temp.Celsius <- Double.Parse(s.Remove(s.LastIndexOf '\'', 2), styles, provider)
        else
            temp.Value <- Double.Parse(s, styles, provider)
        temp

    interface IComparable with
        // IComparable.CompareTo implementation.
        member this.CompareTo(obj: obj) =
            match obj with 
            | null -> 1
            | :? Temperature as temp ->
                this.Value.CompareTo temp.Value
            | _ ->
                invalidArg "obj" "object is not a Temperature"

    interface IFormattable with
        // IFormattable.ToString implementation.
        member this.ToString(format: string, provider: IFormatProvider) =
            match format with
            | "F" ->
                $"{this.Value}'F"
            | "C" ->
                $"{this.Celsius}'C"
            | _ ->
                this.Value.ToString(format, provider)
' Temperature class stores the value as Double
' and delegates most of the functionality 
' to the Double implementation.
Public Class Temperature
    Implements IComparable, IFormattable

    Public Overloads Function CompareTo(ByVal obj As Object) As Integer _
        Implements IComparable.CompareTo

        If TypeOf obj Is Temperature Then
            Dim temp As Temperature = CType(obj, Temperature)

            Return m_value.CompareTo(temp.m_value)
        End If

        Throw New ArgumentException("object is not a Temperature")
    End Function

    Public Overloads Function ToString(ByVal format As String, ByVal provider As IFormatProvider) As String _
        Implements IFormattable.ToString

        If Not (format Is Nothing) Then
            If format.Equals("F") Then
                Return [String].Format("{0}'F", Me.Value.ToString())
            End If
            If format.Equals("C") Then
                Return [String].Format("{0}'C", Me.Celsius.ToString())
            End If
        End If

        Return m_value.ToString(format, provider)
    End Function

    ' Parses the temperature from a string in form
    ' [ws][sign]digits['F|'C][ws]
    Public Shared Function Parse(ByVal s As String, ByVal styles As NumberStyles, ByVal provider As IFormatProvider) As Temperature
        Dim temp As New Temperature()

        If s.TrimEnd().EndsWith("'F") Then
            temp.Value = Double.Parse(s.Remove(s.LastIndexOf("'"c), 2), styles, provider)
        Else
            If s.TrimEnd().EndsWith("'C") Then
                temp.Celsius = Double.Parse(s.Remove(s.LastIndexOf("'"c), 2), styles, provider)
            Else
                temp.Value = Double.Parse(s, styles, provider)
            End If
        End If
        Return temp
    End Function

    ' The value holder
    Protected m_value As Double

    Public Property Value() As Double
        Get
            Return m_value
        End Get
        Set(ByVal Value As Double)
            m_value = Value
        End Set
    End Property

    Public Property Celsius() As Double
        Get
            Return (m_value - 32) / 1.8
        End Get
        Set(ByVal Value As Double)
            m_value = Value * 1.8 + 32
        End Set
    End Property
End Class