Estructura System.Double
En este artículo se proporcionan comentarios adicionales a la documentación de referencia de esta API.
El Double tipo de valor representa un número de 64 bits de precisión doble con valores que van desde 1,79769313486232e308 a positivo 1,79769313486232e308, así como cero positivo o negativo, PositiveInfinity, NegativeInfinityy no un número (NaN). Está pensado para representar valores extremadamente grandes (como distancias entre planetas o galaxias) o extremadamente pequeños (como la masa molecular de una sustancia en kilogramos) y que a menudo son imprecisos (como la distancia de la tierra a otro sistema solar). El Double tipo cumple con el estándar IEC 60559:1989 (IEEE 754) para la aritmética de punto flotante binario.
Representación y precisión de punto flotante
El Double tipo de datos almacena valores de punto flotante de precisión doble en un formato binario de 64 bits, como se muestra en la tabla siguiente:
Parte | bits |
---|---|
Significand o mantissa | 0-51 |
Exponente | 52-62 |
Signo (0 = positivo, 1 = negativo) | 63 |
Al igual que las fracciones decimales no pueden representar con precisión algunos valores fraccionarios (como 1/3 o Math.PI), las fracciones binarias no pueden representar algunos valores fraccionarios. Por ejemplo, 1/10, representado precisamente por .1 como fracción decimal, se representa mediante .001100110011 como una fracción binaria, con el patrón "0011" que se repite en infinito. En este caso, el valor de punto flotante proporciona una representación imprecisa del número que representa. La realización de operaciones matemáticas adicionales en el valor de punto flotante original suele aumentar su falta de precisión. Por ejemplo, si comparamos el resultado de multiplicar .1 por 10 y agregar .1 a .1 nueve veces, vemos que la adición, porque ha implicado ocho operaciones más, ha generado el resultado menos preciso. Tenga en cuenta que esta disparidad solo es evidente si se muestran los dos Double valores mediante la cadena de formato numérico estándar "R", que, si es necesario, muestra todos los 17 dígitos de precisión admitidos por el Double 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
Dado que algunos números no se pueden representar exactamente como valores binarios fraccionarios, los números de punto flotante solo pueden aproximarse a los números reales.
Todos los números de punto flotante también tienen un número limitado de dígitos significativos, que también determina la precisión en que un valor de punto flotante se aproxima a un número real. Un Double valor tiene hasta 15 dígitos decimales de precisión, aunque un máximo de 17 dígitos se mantiene internamente. Esto significa que algunas operaciones de punto flotante pueden carecer de precisión para cambiar un valor de punto flotante. Esto se muestra en el ejemplo siguiente. Define un valor de punto flotante muy grande y, a continuación, agrega el producto de Double.Epsilon y un cuadrílico a él. Sin embargo, el producto es demasiado pequeño para modificar el valor de punto flotante original. Su dígito menos significativo es milésimas, mientras que el dígito más significativo del producto es de 10 a 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
La precisión limitada de un número de punto flotante tiene varias consecuencias:
Dos números de coma flotante que parecen iguales para una precisión determinada podrían no compararse como iguales porque sus dígitos menos significativos sean diferentes. En el ejemplo siguiente, se agregan una serie de números y su total se compara con su total esperado. Aunque los dos valores parecen ser los mismos, una llamada al
Equals
método indica que no lo son.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).
Si cambia los elementos de formato de la Console.WriteLine(String, Object, Object) instrucción de
{0}
y a{0:R}
y{1:R}
{1}
para mostrar todos los dígitos significativos de los dos Double valores, es claro que los dos valores no son iguales debido a una pérdida de precisión durante las operaciones de adición. En este caso, el problema se puede resolver llamando al Math.Round(Double, Int32) método para redondear los Double valores a la precisión deseada antes de realizar la comparación.Una operación matemática o de comparación que usa un número de punto flotante podría no producir el mismo resultado si se usa un número decimal, ya que el número de punto flotante binario podría no ser igual al número decimal. En un ejemplo anterior se ilustra esto mostrando el resultado de multiplicar .1 por 10 y agregar .1 veces.
Cuando la precisión de las operaciones numéricas con valores fraccionarios es importante, puede usar en Decimal lugar del Double tipo . Cuando la precisión de las operaciones numéricas con valores enteros más allá del intervalo de los Int64 tipos o UInt64 es importante, use el BigInteger tipo .
Un valor podría no hacer un recorrido de ida y vuelta si se trata de un número de punto flotante. Se dice que un valor de ida y vuelta si una operación convierte un número de punto flotante original a otro formulario, una operación inversa transforma el formulario convertido en un número de punto flotante y el número de punto flotante final no es igual al número de punto flotante original. Es posible que se produzca un error en el recorrido de ida y vuelta porque uno o varios dígitos menos significativos se pierden o cambian en una conversión. En el ejemplo siguiente, se convierten tres Double valores en cadenas y se guardan en un archivo. Sin embargo, como se muestra en la salida, aunque los valores parezcan idénticos, los valores restaurados no son iguales a los valores originales.
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
En este caso, los valores se pueden recorrer correctamente de ida y vuelta mediante la cadena de formato numérico estándar "G17" para conservar la precisión completa de Double los valores, como se muestra en el ejemplo siguiente.
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
Single los valores tienen menos precisión que Double los valores. Un Single valor que se convierte en un aparentemente equivalente Double a menudo no es igual al Double valor debido a diferencias de precisión. En el ejemplo siguiente, el resultado de operaciones de división idénticas se asigna a y Double un Single valor . Después de convertir el Single valor en , Doubleuna comparación de los dos valores muestra que son desiguales.
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 este problema, use el Double en lugar del Single tipo de datos o use el Round método para que ambos valores tengan la misma precisión.
Además, el resultado de las operaciones aritméticas y de asignación con Double valores puede diferir ligeramente por plataforma debido a la pérdida de precisión del Double tipo. Por ejemplo, el resultado de asignar un valor literal Double puede diferir en las versiones de 32 y 64 bits de .NET. En el ejemplo siguiente se muestra esta diferencia cuando el valor literal -4.42330604244772E-305 y una variable cuyo valor es -4.42330604244772E-305 se asigna a una Double variable. Tenga en cuenta que el resultado del Parse(String) método en este caso no sufre una pérdida de precisión.
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
Prueba de igualdad
Para considerarse igual, dos Double valores deben representar valores idénticos. Sin embargo, debido a las diferencias de precisión entre los valores, o debido a una pérdida de precisión por uno o ambos valores, los valores de punto flotante que se espera que sean idénticos a menudo resultan desiguales debido a las diferencias en sus dígitos menos significativos. Como resultado, llama al Equals método para determinar si dos valores son iguales o llama al CompareTo método para determinar la relación entre dos Double valores, a menudo produce resultados inesperados. Esto es evidente en el ejemplo siguiente, donde dos valores aparentemente iguales Double resultan ser desiguales porque el primero tiene 15 dígitos de precisión, mientras que el segundo tiene 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
Los valores calculados que siguen diferentes rutas de acceso de código y que se manipulan de maneras diferentes suelen resultar desiguales. En el ejemplo siguiente, se calcula un Double valor al cuadrado y, a continuación, se calcula la raíz cuadrada para restaurar el valor original. Un segundo Double se multiplica por 3,51 y se cuadrado antes de que la raíz cuadrada del resultado se divida por 3,51 para restaurar el valor original. Aunque los dos valores parecen ser idénticos, una llamada al Equals(Double) método indica que no son iguales. El uso de la cadena de formato estándar "R" para devolver una cadena de resultado que muestra todos los dígitos significativos de cada valor Double muestra que el segundo valor es .0000000000001 menor que el primero.
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
En los casos en los que es probable que una pérdida de precisión afecte al resultado de una comparación, puede adoptar cualquiera de las siguientes alternativas para llamar al Equals método o CompareTo :
Llame al Math.Round método para asegurarse de que ambos valores tienen la misma precisión. En el ejemplo siguiente se modifica un ejemplo anterior para usar este enfoque para que dos valores fraccionarios sean 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
El problema de precisión todavía se aplica al redondeo de valores de punto medio. Para obtener más información, vea el método Math.Round(Double, Int32, MidpointRounding).
Pruebe la igualdad aproximada en lugar de la igualdad. Esto requiere que defina una cantidad absoluta por la que los dos valores pueden diferir, pero seguir siendo iguales, o que defina una cantidad relativa por la que el valor más pequeño pueda diferir del valor mayor.
Advertencia
Double.Epsilon a veces se usa como medida absoluta de la distancia entre dos Double valores al probar la igualdad. Sin embargo, Double.Epsilon mide el valor más pequeño posible al que se puede agregar o restar de un Double cuyo valor es cero. Para la mayoría de los valores positivos y negativos Double , el valor de Double.Epsilon es demasiado pequeño para detectarse. Por lo tanto, excepto para los valores que son cero, no se recomienda su uso en las pruebas para la igualdad.
En el ejemplo siguiente se usa el último enfoque para definir un
IsApproximatelyEqual
método que prueba la diferencia relativa entre dos valores. También contrasta el resultado de las llamadas alIsApproximatelyEqual
método y al Equals(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 y excepciones de punto flotante
A diferencia de las operaciones con tipos enteros, que producen excepciones en casos de desbordamiento o operaciones ilegales, como la división por cero, las operaciones con valores de punto flotante no inician excepciones. En su lugar, en situaciones excepcionales, el resultado de una operación de punto flotante es cero, infinito positivo, infinito negativo o no un número (NaN):
Si el resultado de una operación de punto flotante es demasiado pequeño para el formato de destino, el resultado es cero. Esto puede ocurrir cuando se multiplican dos números muy pequeños, como se muestra en el ejemplo siguiente.
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
Si la magnitud del resultado de una operación de punto flotante supera el intervalo del formato de destino, el resultado de la operación es PositiveInfinity o NegativeInfinity, según corresponda para el signo del resultado. El resultado de una operación que desborda Double.MaxValue es PositiveInfinityy el resultado de una operación que desborda Double.MinValue es NegativeInfinity, como se muestra en el ejemplo siguiente.
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 también se obtiene de una división por cero con un dividendo positivo y NegativeInfinity resultados de una división por cero con un dividendo negativo.
Si una operación de punto flotante no es válida, el resultado de la operación es NaN. Por ejemplo, NaN los resultados de las siguientes operaciones:
División por cero con un dividendo de cero. Tenga en cuenta que otros casos de división por cero dan como PositiveInfinity resultado o NegativeInfinity.
Cualquier operación de punto flotante con una entrada no válida. Por ejemplo, llamar al Math.Sqrt método con un valor negativo devuelve NaN, como llama al Math.Acos método con un valor mayor que uno o menor que uno negativo.
Cualquier operación con un argumento cuyo valor sea Double.NaN.
Conversiones de tipos
La Double estructura no define ningún operador de conversión explícito o implícito; en su lugar, el compilador implementa las conversiones.
La conversión del valor de cualquier tipo numérico primitivo a es Double una conversión de ampliación y, por tanto, no requiere un operador de conversión explícito ni llamar a un método de conversión a menos que un compilador lo requiera explícitamente. Por ejemplo, el compilador de C# requiere un operador de conversión para las conversiones de Decimal a Double, mientras que el compilador de Visual Basic no lo hace. En el ejemplo siguiente se convierte el valor mínimo o máximo de otros tipos numéricos primitivos en un Double.
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)
Además, los Single valores Single.NaN, Single.PositiveInfinityy Single.NegativeInfinity convierten a Double.NaN, Double.PositiveInfinityy Double.NegativeInfinity, respectivamente.
Tenga en cuenta que la conversión del valor de algunos tipos numéricos a un Double valor puede implicar una pérdida de precisión. Como se muestra en el ejemplo, es posible una pérdida de precisión al convertir Decimallos valores , Int64y UInt64 en Double los valores .
La conversión de un Double valor a un valor de cualquier otro tipo de datos numérico primitivo es una conversión de restricción y requiere un operador de conversión (en C#), un método de conversión (en Visual Basic) o una llamada a un Convert método. Los valores que están fuera del intervalo del tipo de datos de destino, que se definen mediante las propiedades y MaxValue
del tipo MinValue
de destino, se comportan como se muestra en la tabla siguiente.
Tipo de destino | Resultado |
---|---|
Cualquier tipo entero | Excepción OverflowException si la conversión se produce en un contexto comprobado. Si la conversión se produce en un contexto no comprobado (el valor predeterminado en C#), la operación de conversión se realiza correctamente, pero el valor se desborda. |
Decimal | Una OverflowException excepción. |
Single | Single.NegativeInfinity para valores negativos. Single.PositiveInfinity para valores positivos. |
Además, Double.NaN, Double.PositiveInfinityy Double.NegativeInfinity inician una OverflowException excepción para conversiones a enteros en un contexto comprobado, pero estos valores se desbordan cuando se convierten en enteros en un contexto sin marcar. En el caso de las conversiones en Decimal, siempre inician una OverflowExceptionexcepción . Para las conversiones a Single, se convierten en Single.NaN, Single.PositiveInfinityy Single.NegativeInfinity, respectivamente.
Una pérdida de precisión puede dar lugar a la conversión de un Double valor a otro tipo numérico. En el caso de convertir en cualquiera de los tipos enteros, como se muestra en la salida del ejemplo, el componente fraccionario se pierde cuando el Double valor se redondea (como en Visual Basic) o truncado (como en C#). En el caso de las conversiones a Decimal los valores y Single , es posible que el Double valor no tenga una representación precisa en el tipo de datos de destino.
En el ejemplo siguiente se convierte una serie de Double valores en otros tipos numéricos. Las conversiones se producen en un contexto comprobado en Visual Basic (valor predeterminado), en C# (debido a la palabra clave checked ) y en F# (debido al módulo Checked ). La salida del ejemplo muestra el resultado de las conversiones en un contexto no comprobado. Puede realizar conversiones en un contexto sin marcar en Visual Basic mediante la compilación con el /removeintchecks+
modificador del compilador, en C# mediante el comentario de la checked
instrucción y, en F#, comente la open Checked
instrucción .
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 obtener más información sobre la conversión de tipos numéricos, vea Conversión de tipos en tablas de conversión de tipos y .NET.
Funcionalidad de punto flotante
La Double estructura y los tipos relacionados proporcionan métodos para realizar operaciones en las áreas siguientes:
Comparación de valores. Puede llamar al Equals método para determinar si dos Double valores son iguales o el CompareTo método para determinar la relación entre dos valores.
La Double estructura también admite un conjunto completo de operadores de comparación. Por ejemplo, puede probar la igualdad o la desigualdad, o determinar si un valor es mayor o igual que otro. Si uno de los operandos es un tipo numérico distinto de , Doublese convierte en un Double antes de realizar la comparación.
Advertencia
Debido a las diferencias de precisión, dos Double valores que se espera que sean iguales pueden resultar desiguales, lo que afecta al resultado de la comparación. Consulte la sección Prueba de igualdad para obtener más información sobre la comparación de dos Double valores.
También puede llamar a los IsNaNmétodos , IsInfinity, IsPositiveInfinityy IsNegativeInfinity para probar estos valores especiales.
Operaciones matemáticas. Las operaciones aritméticas comunes, como la suma, la resta, la multiplicación y la división, se implementan mediante compiladores de lenguaje y instrucciones del lenguaje intermedio común (CIL), en lugar de por Double métodos. Si uno de los operandos de una operación matemática es un tipo numérico distinto de , Doublese convierte en un Double antes de realizar la operación. El resultado de la operación también es un Double valor.
Otras operaciones matemáticas se pueden realizar llamando a
static
métodos (Shared
en Visual Basic) de la System.Math clase . Incluye métodos adicionales que se usan habitualmente para la aritmética (como , y ), geometría (como Math.Cos y Math.Sin) y cálculo (como Math.Log).Math.SqrtMath.SignMath.AbsTambién puede manipular los bits individuales de un Double valor. El BitConverter.DoubleToInt64Bits método conserva el patrón de bits de un Double valor en un entero de 64 bits. El BitConverter.GetBytes(Double) método devuelve su patrón de bits en una matriz de bytes.
Redondeo. El redondeo se suele usar como técnica para reducir el impacto de las diferencias entre los valores causados por problemas de representación y precisión de punto flotante. Puede redondear un Double valor llamando al Math.Round método .
Formato. Puede convertir un Double valor en su representación de cadena llamando al ToString método o mediante la característica de formato compuesto. Para obtener información sobre cómo las cadenas de formato controlan la representación de cadena de valores de punto flotante, vea los temas Cadenas de formato numérico estándar y Cadenas de formato numérico personalizado.
Análisis de cadenas. Puede convertir la representación de cadena de un valor de punto flotante en un Double valor llamando al Parse método o TryParse . Si se produce un error en la operación de análisis, el Parse método produce una excepción, mientras que el TryParse método devuelve
false
.Conversión de tipos. La Double estructura proporciona una implementación de interfaz explícita para la interfaz, que admite la IConvertible conversión entre dos tipos de datos estándar de .NET. Los compiladores de lenguaje también admiten la conversión implícita de valores de todos los demás tipos numéricos estándar a Double valores. La conversión de un valor de cualquier tipo numérico estándar a es Double una conversión de ampliación y no requiere el usuario de un operador de conversión o método de conversión,
Sin embargo, la conversión de Int64 valores y Single puede implicar una pérdida de precisión. En la tabla siguiente se enumeran las diferencias de precisión para cada uno de estos tipos:
Tipo Precisión máxima Precisión interna Double 15 17 Int64 19 dígitos decimales 19 dígitos decimales Single 7 dígitos decimales 9 dígitos decimales El problema de precisión afecta con más frecuencia a Single los valores que se convierten en Double valores. En el ejemplo siguiente, dos valores generados por operaciones de división idénticas no son iguales porque uno de los valores es un valor de punto flotante de precisión sencilla convertido en .Double
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
Ejemplos
En el ejemplo de código siguiente se muestra el 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