Compartir a través de


Estructura System.Single

En este artículo se proporcionan comentarios adicionales a la documentación de referencia de esta API.

El Single tipo de valor representa un número de 32 bits de precisión única con valores que van desde 3,402823e38 negativo hasta 3,402823e38, 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 Single tipo cumple con el estándar IEC 60559:1989 (IEEE 754) para la aritmética de punto flotante binario.

System.Single proporciona métodos para comparar instancias de este tipo, convertir el valor de una instancia en su representación de cadena y convertir la representación de cadena de un número en una instancia de este tipo. Para obtener información sobre cómo los códigos de especificación de formato controlan la representación de cadena de los tipos de valor, vea Formato de tipos, Cadenas de formato numérico estándar y Cadenas de formato numérico personalizado.

Representación y precisión de punto flotante

El Single tipo de datos almacena valores de punto flotante de precisión única en un formato binario de 32 bits, como se muestra en la tabla siguiente:

Parte bits
Significand o mantissa 0-22
Exponente 23-30
Signo (0 = positivo, 1 = negativo) 31

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, 2/10, representado precisamente por .2 como fracción decimal, se representa mediante .0011111001001100 como una fracción binaria, con el patrón "1100" 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 a menudo aumenta su falta de precisión. Por ejemplo, si compara los resultados de multiplicar .3 por 10 y agregar .3 a .3 nueve veces, verá que la adición produce el resultado menos preciso, ya que implica ocho operaciones más que la multiplicación. Tenga en cuenta que esta disparidad solo es evidente si se muestran los dos Single valores mediante la cadena de formato numérico estándar "R", que, si es necesario, muestra todos los 9 dígitos de precisión admitidos por el Single tipo.

using System;

public class Example12
{
    public static void Main()
    {
        Single value = .2f;
        Single result1 = value * 10f;
        Single result2 = 0f;
        for (int ctr = 1; ctr <= 10; ctr++)
            result2 += value;

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

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

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 tienen un número limitado de dígitos significativos, que también determina con qué precisión un valor de punto flotante se aproxima a un número real. Un Single valor tiene hasta 7 dígitos decimales de precisión, aunque se mantiene internamente un máximo de 9 dígitos. Esto significa que algunas operaciones de punto flotante pueden carecer de precisión para cambiar un valor de punto flotante. En el ejemplo siguiente se define un valor de punto flotante de precisión sencilla grande y, a continuación, se agrega el producto de Single.Epsilon y un cuadrílico. 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 30.

using System;

public class Example13
{
    public static void Main()
    {
        Single value = 123.456f;
        Single additional = Single.Epsilon * 1e15f;
        Console.WriteLine($"{value} + {additional} = {value + additional}");
    }
}
// The example displays the following output:
//    123.456 + 1.401298E-30 = 123.456
open System

let value = 123.456f
let additional = Single.Epsilon * 1e15f
printfn $"{value} + {additional} = {value + additional}"
// The example displays the following output:
//    123.456 + 1.401298E-30 = 123.456
Module Example
   Public Sub Main()
      Dim value As Single = 123.456
      Dim additional As Single = Single.Epsilon * 1e15
      Console.WriteLine($"{value} + {additional} = {value + additional}")
   End Sub
End Module
' The example displays the following output:
'   123.456 + 1.401298E-30 = 123.456

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

    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 Single 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 Single 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 .3 por 10 y agregar .3 a .3 nueve veces.

    Cuando la precisión de las operaciones numéricas con valores fraccionarios es importante, use el Decimal tipo en lugar del Single 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 en 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 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 Single valores en cadenas y se guardan en un archivo. Como se muestra en la salida, aunque los valores parecen ser idénticos, los valores restaurados no son iguales a los valores originales.

    using System;
    using System.IO;
    
    public class Example10
    {
        public static void Main()
        {
            StreamWriter sw = new StreamWriter(@".\Singles.dat");
            Single[] values = { 3.2f / 1.11f, 1.0f / 3f, (float)Math.PI };
            for (int ctr = 0; ctr < values.Length; ctr++)
            {
                sw.Write(values[ctr].ToString());
                if (ctr != values.Length - 1)
                    sw.Write("|");
            }
            sw.Close();
    
            Single[] restoredValues = new Single[values.Length];
            StreamReader sr = new StreamReader(@".\Singles.dat");
            string temp = sr.ReadToEnd();
            string[] tempStrings = temp.Split('|');
            for (int ctr = 0; ctr < tempStrings.Length; ctr++)
                restoredValues[ctr] = Single.Parse(tempStrings[ctr]);
    
            for (int ctr = 0; ctr < values.Length; ctr++)
                Console.WriteLine("{0} {2} {1}", values[ctr],
                                  restoredValues[ctr],
                                  values[ctr].Equals(restoredValues[ctr]) ? "=" : "<>");
        }
    }
    // The example displays the following output:
    //       2.882883 <> 2.882883
    //       0.3333333 <> 0.3333333
    //       3.141593 <> 3.141593
    
    open System
    open System.IO
    
    let values = [| 3.2f / 1.11f; 1f / 3f; MathF.PI |]
    
    do
        use sw = new StreamWriter(@".\Singles.dat")
        for i = 0 to values.Length - 1 do
            sw.Write(string values[i])
            if i <> values.Length - 1 then
                sw.Write "|"
    
    let restoredValues =
        use sr = new StreamReader(@".\Singles.dat")
        sr.ReadToEnd().Split '|'
        |> Array.map Single.Parse
    
    for i = 0 to values.Length - 1 do
        printfn $"""{values[i]} {if values[i].Equals restoredValues[i] then "=" else "<>"} {restoredValues[i]}"""
                        
    // The example displays the following output:
    //       2.882883 <> 2.882883
    //       0.3333333 <> 0.3333333
    //       3.141593 <> 3.141593
    
    Imports System.IO
    
    Module Example11
        Public Sub Main()
            Dim sw As New StreamWriter(".\Singles.dat")
            Dim values() As Single = {3.2 / 1.11, 1.0 / 3, CSng(Math.PI)}
            For ctr As Integer = 0 To values.Length - 1
                sw.Write(values(ctr).ToString())
                If ctr <> values.Length - 1 Then sw.Write("|")
            Next
            sw.Close()
    
            Dim restoredValues(values.Length - 1) As Single
            Dim sr As New StreamReader(".\Singles.dat")
            Dim temp As String = sr.ReadToEnd()
            Dim tempStrings() As String = temp.Split("|"c)
            For ctr As Integer = 0 To tempStrings.Length - 1
                restoredValues(ctr) = Single.Parse(tempStrings(ctr))
            Next
    
            For ctr As Integer = 0 To values.Length - 1
                Console.WriteLine("{0} {2} {1}", values(ctr),
                               restoredValues(ctr),
                               If(values(ctr).Equals(restoredValues(ctr)), "=", "<>"))
            Next
        End Sub
    End Module
    ' The example displays the following output:
    '        2.882883 <> 2.882883
    '        0.3333333 <> 0.3333333
    '        3.141593 <> 3.141593
    

    En este caso, los valores se pueden recorrer correctamente de ida y vuelta mediante la cadena de formato numérico estándar "G9" para conservar la precisión completa de Single los valores, como se muestra en el ejemplo siguiente.

    using System;
    using System.IO;
    
    public class Example11
    {
        public static void Main()
        {
            StreamWriter sw = new StreamWriter(@".\Singles.dat");
            Single[] values = { 3.2f / 1.11f, 1.0f / 3f, (float)Math.PI };
            for (int ctr = 0; ctr < values.Length; ctr++)
                sw.Write("{0:G9}{1}", values[ctr], ctr < values.Length - 1 ? "|" : "");
    
            sw.Close();
    
            Single[] restoredValues = new Single[values.Length];
            StreamReader sr = new StreamReader(@".\Singles.dat");
            string temp = sr.ReadToEnd();
            string[] tempStrings = temp.Split('|');
            for (int ctr = 0; ctr < tempStrings.Length; ctr++)
                restoredValues[ctr] = Single.Parse(tempStrings[ctr]);
    
            for (int ctr = 0; ctr < values.Length; ctr++)
                Console.WriteLine("{0} {2} {1}", values[ctr],
                                  restoredValues[ctr],
                                  values[ctr].Equals(restoredValues[ctr]) ? "=" : "<>");
        }
    }
    // The example displays the following output:
    //       2.882883 = 2.882883
    //       0.3333333 = 0.3333333
    //       3.141593 = 3.141593
    
    open System
    open System.IO
    
    let values = [| 3.2f / 1.11f; 1f / 3f; MathF.PI |]
    
    do
        use sw = new StreamWriter(@".\Singles.dat")
        for i = 0 to values.Length - 1 do
            sw.Write $"""{values[i]:G9}{if i < values.Length - 1 then "|" else ""}"""
        
        
    let restoredValues =
        use sr = new StreamReader(@".\Singles.dat")
        sr.ReadToEnd().Split '|'
        |> Array.map Single.Parse
    
    for i = 0 to values.Length - 1 do
        printfn $"""{values[i]} {if values[i].Equals restoredValues[i] then "=" else "<>"} {restoredValues[i]}"""
    // The example displays the following output:
    //       2.882883 = 2.882883
    //       0.3333333 = 0.3333333
    //       3.141593 = 3.141593
    
    Imports System.IO
    
    Module Example12
        Public Sub Main()
            Dim sw As New StreamWriter(".\Singles.dat")
            Dim values() As Single = {3.2 / 1.11, 1.0 / 3, CSng(Math.PI)}
            For ctr As Integer = 0 To values.Length - 1
                sw.Write("{0:G9}{1}", values(ctr),
                      If(ctr < values.Length - 1, "|", ""))
            Next
            sw.Close()
    
            Dim restoredValues(values.Length - 1) As Single
            Dim sr As New StreamReader(".\Singles.dat")
            Dim temp As String = sr.ReadToEnd()
            Dim tempStrings() As String = temp.Split("|"c)
            For ctr As Integer = 0 To tempStrings.Length - 1
                restoredValues(ctr) = Single.Parse(tempStrings(ctr))
            Next
    
            For ctr As Integer = 0 To values.Length - 1
                Console.WriteLine("{0} {2} {1}", values(ctr),
                               restoredValues(ctr),
                               If(values(ctr).Equals(restoredValues(ctr)), "=", "<>"))
            Next
        End Sub
    End Module
    ' The example displays the following output:
    '       2.882883 = 2.882883
    '       0.3333333 = 0.3333333
    '       3.141593 = 3.141593
    
  • Single 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 un Double valor y 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 tipo de datos en lugar del Single tipo de datos o use el Round método para que ambos valores tengan la misma precisión.

Prueba de igualdad

Para considerarse igual, dos Single 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 suelen ser iguales 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 Single valores, a menudo produce resultados inesperados. Esto es evidente en el ejemplo siguiente, donde dos valores aparentemente iguales Single resultan ser desiguales, porque el primer valor tiene 7 dígitos de precisión, mientras que el segundo valor tiene 9.

using System;

public class Example
{
   public static void Main()
   {
      float value1 = .3333333f;
      float value2 = 1.0f/3;
      Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2));
   }
}
// The example displays the following output:
//        0.3333333 = 0.333333343: False
let value1 = 0.3333333f
let value2 = 1f / 3f
printfn $"{value1:R} = {value2:R}: {value1.Equals value2}"
// The example displays the following output:
//        0.3333333 = 0.333333343: False
Module Example1
    Public Sub Main()
        Dim value1 As Single = 0.3333333
        Dim value2 As Single = 1 / 3
        Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2))
    End Sub
End Module
' The example displays the following output:
'       0.3333333 = 0.333333343: False

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 Single valor al cuadrado y, a continuación, se calcula la raíz cuadrada para restaurar el valor original. Un segundo Single 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(Single) método indica que no son iguales. El uso de la cadena de formato estándar "G9" para devolver una cadena de resultado que muestra todos los dígitos significativos de cada Single valor muestra que el segundo valor es .0000000000001 menor que el primero.

using System;

public class Example1
{
    public static void Main()
    {
        float value1 = 10.201438f;
        value1 = (float)Math.Sqrt((float)Math.Pow(value1, 2));
        float value2 = (float)Math.Pow((float)value1 * 3.51f, 2);
        value2 = ((float)Math.Sqrt(value2)) / 3.51f;
        Console.WriteLine("{0} = {1}: {2}\n",
                          value1, value2, value1.Equals(value2));
        Console.WriteLine("{0:G9} = {1:G9}", value1, value2);
    }
}
// The example displays the following output:
//       10.20144 = 10.20144: False
//       
//       10.201438 = 10.2014389
let value1 = 
    10.201438f ** 2f
    |> sqrt

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

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

En los casos en los que es probable que una pérdida de precisión afecte al resultado de una comparación, puede usar las técnicas siguientes en lugar de 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()
        {
            float value1 = .3333333f;
            float value2 = 1.0f / 3;
            int precision = 7;
            value1 = (float)Math.Round(value1, precision);
            value2 = (float)Math.Round(value2, precision);
            Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2));
        }
    }
    // The example displays the following output:
    //        0.3333333 = 0.3333333: True
    
    open System
    
    let value1 = 0.3333333f
    let value2 = 1f / 3f
    let precision = 7
    let value1r = Math.Round(float value1, precision) |> float32
    let value2r = Math.Round(float value2, precision) |> float32
    printfn $"{value1:R} = {value2:R}: {value1.Equals value2}"
    // The example displays the following output:
    //        0.3333333 = 0.3333333: True
    
    Module Example3
        Public Sub Main()
            Dim value1 As Single = 0.3333333
            Dim value2 As Single = 1 / 3
            Dim precision As Integer = 7
            value1 = CSng(Math.Round(value1, precision))
            value2 = CSng(Math.Round(value2, precision))
            Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2, value1.Equals(value2))
        End Sub
    End Module
    ' The example displays the following output:
    '       0.3333333 = 0.3333333: True
    

    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. Esta técnica 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

    Single.Epsilon a veces se usa como medida absoluta de la distancia entre dos Single valores al probar la igualdad. Sin embargo, Single.Epsilon mide el valor más pequeño posible al que se puede agregar o restar de un Single cuyo valor es cero. Para la mayoría de los valores positivos y negativos Single , el valor de Single.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 al IsApproximatelyEqual método y al Equals(Single) método .

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

Valores y excepciones de punto flotante

Las operaciones con valores de punto flotante no inician excepciones, a diferencia de las operaciones con tipos enteros, que inician excepciones en casos de operaciones ilegales, como divisiones por cero o desbordamiento. En su lugar, en estas situaciones, 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 de punto flotante muy pequeños, como se muestra en el ejemplo siguiente.

    using System;
    
    public class Example6
    {
        public static void Main()
        {
            float value1 = 1.163287e-36f;
            float value2 = 9.164234e-25f;
            float result = value1 * value2;
            Console.WriteLine("{0} * {1} = {2}", value1, value2, result);
            Console.WriteLine("{0} = 0: {1}", result, result.Equals(0.0f));
        }
    }
    // The example displays the following output:
    //       1.163287E-36 * 9.164234E-25 = 0
    //       0 = 0: True
    
    let value1 = 1.163287e-36f
    let value2 = 9.164234e-25f
    let result = value1 * value2
    printfn $"{value1} * {value2} = {result}"
    printfn $"{result} = 0: {result.Equals(0f)}"
    // The example displays the following output:
    //       1.163287E-36 * 9.164234E-25 = 0
    //       0 = 0: True
    
    Module Example7
        Public Sub Main()
            Dim value1 As Single = 1.163287E-36
            Dim value2 As Single = 9.164234E-25
            Dim result As Single = value1 * value2
            Console.WriteLine("{0} * {1} = {2:R}", value1, value2, result)
            Console.WriteLine("{0} = 0: {1}", result, result.Equals(0))
        End Sub
    End Module
    ' The example displays the following output:
    '       1.163287E-36 * 9.164234E-25 = 0
    '       0 = 0: True
    
  • 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 Single.MaxValue es PositiveInfinityy el resultado de una operación que desborda Single.MinValue es NegativeInfinity, como se muestra en el ejemplo siguiente.

    using System;
    
    public class Example7
    {
        public static void Main()
        {
            float value1 = 3.065e35f;
            float value2 = 6.9375e32f;
            float result = value1 * value2;
            Console.WriteLine("PositiveInfinity: {0}",
                               Single.IsPositiveInfinity(result));
            Console.WriteLine("NegativeInfinity: {0}\n",
                              Single.IsNegativeInfinity(result));
    
            value1 = -value1;
            result = value1 * value2;
            Console.WriteLine("PositiveInfinity: {0}",
                               Single.IsPositiveInfinity(result));
            Console.WriteLine("NegativeInfinity: {0}",
                              Single.IsNegativeInfinity(result));
        }
    }
    
    // The example displays the following output:
    //       PositiveInfinity: True
    //       NegativeInfinity: False
    //       
    //       PositiveInfinity: False
    //       NegativeInfinity: True
    
    open System
    
    let value1 = 3.065e35f
    let value2 = 6.9375e32f
    let result = value1 * value2
    printfn $"PositiveInfinity: {Single.IsPositiveInfinity result}" 
    printfn $"NegativeInfinity: {Single.IsNegativeInfinity result}\n"
    
    let value3 = -value1
    let result2 = value3 * value2
    printfn $"PositiveInfinity: {Single.IsPositiveInfinity result}" 
    printfn $"NegativeInfinity: {Single.IsNegativeInfinity result}" 
    
    // The example displays the following output:
    //       PositiveInfinity: True
    //       NegativeInfinity: False
    //       
    //       PositiveInfinity: False
    //       NegativeInfinity: True
    
    Module Example8
        Public Sub Main()
            Dim value1 As Single = 3.065E+35
            Dim value2 As Single = 6.9375E+32
            Dim result As Single = value1 * value2
            Console.WriteLine("PositiveInfinity: {0}",
                             Single.IsPositiveInfinity(result))
            Console.WriteLine("NegativeInfinity: {0}",
                            Single.IsNegativeInfinity(result))
            Console.WriteLine()
            value1 = -value1
            result = value1 * value2
            Console.WriteLine("PositiveInfinity: {0}",
                             Single.IsPositiveInfinity(result))
            Console.WriteLine("NegativeInfinity: {0}",
                            Single.IsNegativeInfinity(result))
        End Sub
    End Module
    ' The example displays the following output:
    '       PositiveInfinity: True
    '       NegativeInfinity: False
    '       
    '       PositiveInfinity: False
    '       NegativeInfinity: True
    

    PositiveInfinity 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 entrada no válida. Por ejemplo, si intenta buscar la raíz cuadrada de un valor negativo, devuelve NaN.
    • Cualquier operación con un argumento cuyo valor sea Single.NaN.

Conversiones de tipos

La Single estructura no define ningún operador de conversión explícito o implícito; en su lugar, el compilador implementa las conversiones.

En la tabla siguiente se enumeran las posibles conversiones de un valor de los otros tipos numéricos primitivos en un Single valor, también se indica si la conversión está ampliando o limitando y si el resultado Single puede tener menos precisión que el valor original.

Conversión de Ampliación y restricción Posible pérdida de precisión
Byte Widening No
Decimal Widening

Tenga en cuenta que C# requiere un operador de conversión.
Sí. Decimal admite 29 dígitos decimales de precisión; Single admite 9.
Double Estrechamiento; Los valores fuera del intervalo se convierten en Double.NegativeInfinity o Double.PositiveInfinity. Sí. Double admite 17 dígitos decimales de precisión; Single admite 9.
Int16 Widening No
Int32 Widening Sí. Int32 admite 10 dígitos decimales de precisión; Single admite 9.
Int64 Widening Sí. Int64 admite 19 dígitos decimales de precisión; Single admite 9.
SByte Widening No
UInt16 Widening No
UInt32 Widening Sí. UInt32 admite 10 dígitos decimales de precisión; Single admite 9.
UInt64 Widening Sí. Int64 admite 20 dígitos decimales de precisión; Single admite 9.

En el ejemplo siguiente se convierte el valor mínimo o máximo de otros tipos numéricos primitivos en un Single valor.

using System;

public class Example4
{
    public static void Main()
    {
        dynamic[] values = { Byte.MinValue, Byte.MaxValue, Decimal.MinValue,
                           Decimal.MaxValue, Double.MinValue, Double.MaxValue,
                           Int16.MinValue, Int16.MaxValue, Int32.MinValue,
                           Int32.MaxValue, Int64.MinValue, Int64.MaxValue,
                           SByte.MinValue, SByte.MaxValue, UInt16.MinValue,
                           UInt16.MaxValue, UInt32.MinValue, UInt32.MaxValue,
                           UInt64.MinValue, UInt64.MaxValue };
        float sngValue;
        foreach (var value in values)
        {
            if (value.GetType() == typeof(Decimal) ||
                value.GetType() == typeof(Double))
                sngValue = (float)value;
            else
                sngValue = value;
            Console.WriteLine("{0} ({1}) --> {2:R} ({3})",
                              value, value.GetType().Name,
                              sngValue, sngValue.GetType().Name);
        }
    }
}
// The example displays the following output:
//       0 (Byte) --> 0 (Single)
//       255 (Byte) --> 255 (Single)
//       -79228162514264337593543950335 (Decimal) --> -7.92281625E+28 (Single)
//       79228162514264337593543950335 (Decimal) --> 7.92281625E+28 (Single)
//       -1.79769313486232E+308 (Double) --> -Infinity (Single)
//       1.79769313486232E+308 (Double) --> Infinity (Single)
//       -32768 (Int16) --> -32768 (Single)
//       32767 (Int16) --> 32767 (Single)
//       -2147483648 (Int32) --> -2.14748365E+09 (Single)
//       2147483647 (Int32) --> 2.14748365E+09 (Single)
//       -9223372036854775808 (Int64) --> -9.223372E+18 (Single)
//       9223372036854775807 (Int64) --> 9.223372E+18 (Single)
//       -128 (SByte) --> -128 (Single)
//       127 (SByte) --> 127 (Single)
//       0 (UInt16) --> 0 (Single)
//       65535 (UInt16) --> 65535 (Single)
//       0 (UInt32) --> 0 (Single)
//       4294967295 (UInt32) --> 4.2949673E+09 (Single)
//       0 (UInt64) --> 0 (Single)
//       18446744073709551615 (UInt64) --> 1.84467441E+19 (Single)
open System

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

for value in values do
    let sngValue = 
        match value with
        | :? byte as v -> float32 v
        | :? decimal as v -> float32 v
        | :? double as v -> float32 v
        | :? int16 as v -> float32 v
        | :? int as v -> float32 v
        | :? int64 as v -> float32 v
        | :? int8 as v -> float32 v
        | :? uint16 as v -> float32 v
        | :? uint as v -> float32 v
        | :? uint64 as v -> float32 v
        | _ -> raise (NotImplementedException "Unknown Type")
    printfn $"{value} ({value.GetType().Name}) --> {sngValue:R} ({sngValue.GetType().Name})"
// The example displays the following output:
//       0 (Byte) --> 0 (Single)
//       255 (Byte) --> 255 (Single)
//       -79228162514264337593543950335 (Decimal) --> -7.92281625E+28 (Single)
//       79228162514264337593543950335 (Decimal) --> 7.92281625E+28 (Single)
//       -1.79769313486232E+308 (Double) --> -Infinity (Single)
//       1.79769313486232E+308 (Double) --> Infinity (Single)
//       -32768 (Int16) --> -32768 (Single)
//       32767 (Int16) --> 32767 (Single)
//       -2147483648 (Int32) --> -2.14748365E+09 (Single)
//       2147483647 (Int32) --> 2.14748365E+09 (Single)
//       -9223372036854775808 (Int64) --> -9.223372E+18 (Single)
//       9223372036854775807 (Int64) --> 9.223372E+18 (Single)
//       -128 (SByte) --> -128 (Single)
//       127 (SByte) --> 127 (Single)
//       0 (UInt16) --> 0 (Single)
//       65535 (UInt16) --> 65535 (Single)
//       0 (UInt32) --> 0 (Single)
//       4294967295 (UInt32) --> 4.2949673E+09 (Single)
//       0 (UInt64) --> 0 (Single)
//       18446744073709551615 (UInt64) --> 1.84467441E+19 (Single)
Module Example5
    Public Sub Main()
        Dim values() As Object = {Byte.MinValue, Byte.MaxValue, Decimal.MinValue,
                                 Decimal.MaxValue, Double.MinValue, Double.MaxValue,
                                 Int16.MinValue, Int16.MaxValue, Int32.MinValue,
                                 Int32.MaxValue, Int64.MinValue, Int64.MaxValue,
                                 SByte.MinValue, SByte.MaxValue, UInt16.MinValue,
                                 UInt16.MaxValue, UInt32.MinValue, UInt32.MaxValue,
                                 UInt64.MinValue, UInt64.MaxValue}
        Dim sngValue As Single
        For Each value In values
            If value.GetType() = GetType(Double) Then
                sngValue = CSng(value)
            Else
                sngValue = value
            End If
            Console.WriteLine("{0} ({1}) --> {2:R} ({3})",
                           value, value.GetType().Name,
                           sngValue, sngValue.GetType().Name)
        Next
    End Sub
End Module
' The example displays the following output:
'       0 (Byte) --> 0 (Single)
'       255 (Byte) --> 255 (Single)
'       -79228162514264337593543950335 (Decimal) --> -7.92281625E+28 (Single)
'       79228162514264337593543950335 (Decimal) --> 7.92281625E+28 (Single)
'       -1.79769313486232E+308 (Double) --> -Infinity (Single)
'       1.79769313486232E+308 (Double) --> Infinity (Single)
'       -32768 (Int16) --> -32768 (Single)
'       32767 (Int16) --> 32767 (Single)
'       -2147483648 (Int32) --> -2.14748365E+09 (Single)
'       2147483647 (Int32) --> 2.14748365E+09 (Single)
'       -9223372036854775808 (Int64) --> -9.223372E+18 (Single)
'       9223372036854775807 (Int64) --> 9.223372E+18 (Single)
'       -128 (SByte) --> -128 (Single)
'       127 (SByte) --> 127 (Single)
'       0 (UInt16) --> 0 (Single)
'       65535 (UInt16) --> 65535 (Single)
'       0 (UInt32) --> 0 (Single)
'       4294967295 (UInt32) --> 4.2949673E+09 (Single)
'       0 (UInt64) --> 0 (Single)
'       18446744073709551615 (UInt64) --> 1.84467441E+19 (Single)

Además, los Double valores Double.NaN, Double.PositiveInfinityy Double.NegativeInfinity convierten a Single.NaN, Single.PositiveInfinityy Single.NegativeInfinity, respectivamente.

Tenga en cuenta que la conversión del valor de algunos tipos numéricos a un Single 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 , Double, Int64Int32, , UInt32y UInt64 en Single los valores .

La conversión de un Single valor a es Double una conversión de ampliación. La conversión puede provocar una pérdida de precisión si el Double tipo no tiene una representación precisa para el Single valor.

La conversión de un Single valor a un valor de cualquier tipo de datos numérico primitivo distinto de es Double una conversión de restricción y requiere un operador de conversión (en C#) o un método de conversión (en Visual Basic). 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,

Además, Single.NaN, Single.PositiveInfinityy Single.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 Double, se convierten en Double.NaN, Double.PositiveInfinityy Double.NegativeInfinity, respectivamente.

Tenga en cuenta que una pérdida de precisión puede dar lugar a la conversión de un Single valor a otro tipo numérico. En el caso de convertir valores no enteros Single , como se muestra en la salida del ejemplo, el componente fraccionario se pierde cuando el Single valor se redondea (como en Visual Basic) o trunca (como en C# y F#). En el caso de las conversiones a Decimal valores, es posible que el Single valor no tenga una representación precisa en el tipo de datos de destino.

En el ejemplo siguiente se convierte una serie de Single 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 a la open Checked instrucción ). 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()
    {
        float[] values = { Single.MinValue, -67890.1234f, -12345.6789f,
                         12345.6789f, 67890.1234f, Single.MaxValue,
                         Single.NaN, Single.PositiveInfinity,
                         Single.NegativeInfinity };
        checked
        {
            foreach (var value in values)
            {
                try
                {
                    Int64 lValue = (long)value;
                    Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
                                      value, value.GetType().Name,
                                      lValue, lValue.GetType().Name);
                }
                catch (OverflowException)
                {
                    Console.WriteLine("Unable to convert {0} to Int64.", value);
                }
                try
                {
                    UInt64 ulValue = (ulong)value;
                    Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
                                      value, value.GetType().Name,
                                      ulValue, ulValue.GetType().Name);
                }
                catch (OverflowException)
                {
                    Console.WriteLine("Unable to convert {0} to UInt64.", value);
                }
                try
                {
                    Decimal dValue = (decimal)value;
                    Console.WriteLine("{0} ({1}) --> {2} ({3})",
                                      value, value.GetType().Name,
                                      dValue, dValue.GetType().Name);
                }
                catch (OverflowException)
                {
                    Console.WriteLine("Unable to convert {0} to Decimal.", value);
                }

                Double dblValue = value;
                Console.WriteLine("{0} ({1}) --> {2} ({3})",
                                  value, value.GetType().Name,
                                  dblValue, dblValue.GetType().Name);
                Console.WriteLine();
            }
        }
    }
}
// The example displays the following output for conversions performed
// in a checked context:
//       Unable to convert -3.402823E+38 to Int64.
//       Unable to convert -3.402823E+38 to UInt64.
//       Unable to convert -3.402823E+38 to Decimal.
//       -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
//
//       -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
//       Unable to convert -67890.13 to UInt64.
//       -67890.13 (Single) --> -67890.12 (Decimal)
//       -67890.13 (Single) --> -67890.125 (Double)
//
//       -12345.68 (Single) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
//       Unable to convert -12345.68 to UInt64.
//       -12345.68 (Single) --> -12345.68 (Decimal)
//       -12345.68 (Single) --> -12345.6787109375 (Double)
//
//       12345.68 (Single) --> 12345 (0x0000000000003039) (Int64)
//       12345.68 (Single) --> 12345 (0x0000000000003039) (UInt64)
//       12345.68 (Single) --> 12345.68 (Decimal)
//       12345.68 (Single) --> 12345.6787109375 (Double)
//
//       67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
//       67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
//       67890.13 (Single) --> 67890.12 (Decimal)
//       67890.13 (Single) --> 67890.125 (Double)
//
//       Unable to convert 3.402823E+38 to Int64.
//       Unable to convert 3.402823E+38 to UInt64.
//       Unable to convert 3.402823E+38 to Decimal.
//       3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
//
//       Unable to convert NaN to Int64.
//       Unable to convert NaN to UInt64.
//       Unable to convert NaN to Decimal.
//       NaN (Single) --> NaN (Double)
//
//       Unable to convert Infinity to Int64.
//       Unable to convert Infinity to UInt64.
//       Unable to convert Infinity to Decimal.
//       Infinity (Single) --> Infinity (Double)
//
//       Unable to convert -Infinity to Int64.
//       Unable to convert -Infinity to UInt64.
//       Unable to convert -Infinity to Decimal.
//       -Infinity (Single) --> -Infinity (Double)
// The example displays the following output for conversions performed
// in an unchecked context:
//       -3.402823E+38 (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       -3.402823E+38 (Single) --> 9223372036854775808 (0x8000000000000000) (UInt64)
//       Unable to convert -3.402823E+38 to Decimal.
//       -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
//
//       -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
//       -67890.13 (Single) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE) (UInt64)
//       -67890.13 (Single) --> -67890.12 (Decimal)
//       -67890.13 (Single) --> -67890.125 (Double)
//
//       -12345.68 (Single) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
//       -12345.68 (Single) --> 18446744073709539271 (0xFFFFFFFFFFFFCFC7) (UInt64)
//       -12345.68 (Single) --> -12345.68 (Decimal)
//       -12345.68 (Single) --> -12345.6787109375 (Double)
//
//       12345.68 (Single) --> 12345 (0x0000000000003039) (Int64)
//       12345.68 (Single) --> 12345 (0x0000000000003039) (UInt64)
//       12345.68 (Single) --> 12345.68 (Decimal)
//       12345.68 (Single) --> 12345.6787109375 (Double)
//
//       67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
//       67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
//       67890.13 (Single) --> 67890.12 (Decimal)
//       67890.13 (Single) --> 67890.125 (Double)
//
//       3.402823E+38 (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       3.402823E+38 (Single) --> 0 (0x0000000000000000) (UInt64)
//       Unable to convert 3.402823E+38 to Decimal.
//       3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
//
//       NaN (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       NaN (Single) --> 0 (0x0000000000000000) (UInt64)
//       Unable to convert NaN to Decimal.
//       NaN (Single) --> NaN (Double)
//
//       Infinity (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       Infinity (Single) --> 0 (0x0000000000000000) (UInt64)
//       Unable to convert Infinity to Decimal.
//       Infinity (Single) --> Infinity (Double)
//
//       -Infinity (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       -Infinity (Single) --> 9223372036854775808 (0x8000000000000000) (UInt64)
//       Unable to convert -Infinity to Decimal.
//       -Infinity (Single) --> -Infinity (Double)
open System
open Checked

let values = 
    [ Single.MinValue; -67890.1234f; -12345.6789f
      12345.6789f; 67890.1234f; Single.MaxValue
      Single.NaN; Single.PositiveInfinity
      Single.NegativeInfinity ]

for value in values do
    try
        let lValue = int64 value
        printfn $"{value} ({value.GetType().Name}) --> {lValue} (0x{lValue:X16}) ({lValue.GetType().Name})"
    with :? OverflowException ->
        printfn $"Unable to convert {value} to Int64."
    try
        let ulValue = uint64 value
        printfn $"{value} ({value.GetType().Name}) --> {ulValue} (0x{ulValue:X16}) ({ulValue.GetType().Name})"
    with :? OverflowException ->
        printfn $"Unable to convert {value} to UInt64."
    try
        let dValue = decimal value
        printfn $"{value} ({value.GetType().Name}) --> {dValue} ({dValue.GetType().Name})"
    with :? OverflowException ->
        printfn $"Unable to convert {value} to Decimal."

    let dblValue = double value
    printfn $"{value} ({value.GetType().Name}) --> {dblValue} ({dblValue.GetType().Name})\n"
// The example displays the following output for conversions performed
// in a checked context:
//       Unable to convert -3.402823E+38 to Int64.
//       Unable to convert -3.402823E+38 to UInt64.
//       Unable to convert -3.402823E+38 to Decimal.
//       -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
//
//       -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
//       Unable to convert -67890.13 to UInt64.
//       -67890.13 (Single) --> -67890.12 (Decimal)
//       -67890.13 (Single) --> -67890.125 (Double)
//
//       -12345.68 (Single) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
//       Unable to convert -12345.68 to UInt64.
//       -12345.68 (Single) --> -12345.68 (Decimal)
//       -12345.68 (Single) --> -12345.6787109375 (Double)
//
//       12345.68 (Single) --> 12345 (0x0000000000003039) (Int64)
//       12345.68 (Single) --> 12345 (0x0000000000003039) (UInt64)
//       12345.68 (Single) --> 12345.68 (Decimal)
//       12345.68 (Single) --> 12345.6787109375 (Double)
//
//       67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
//       67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
//       67890.13 (Single) --> 67890.12 (Decimal)
//       67890.13 (Single) --> 67890.125 (Double)
//
//       Unable to convert 3.402823E+38 to Int64.
//       Unable to convert 3.402823E+38 to UInt64.
//       Unable to convert 3.402823E+38 to Decimal.
//       3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
//
//       Unable to convert NaN to Int64.
//       Unable to convert NaN to UInt64.
//       Unable to convert NaN to Decimal.
//       NaN (Single) --> NaN (Double)
//
//       Unable to convert Infinity to Int64.
//       Unable to convert Infinity to UInt64.
//       Unable to convert Infinity to Decimal.
//       Infinity (Single) --> Infinity (Double)
//
//       Unable to convert -Infinity to Int64.
//       Unable to convert -Infinity to UInt64.
//       Unable to convert -Infinity to Decimal.
//       -Infinity (Single) --> -Infinity (Double)
// The example displays the following output for conversions performed
// in an unchecked context:
//       -3.402823E+38 (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       -3.402823E+38 (Single) --> 9223372036854775808 (0x8000000000000000) (UInt64)
//       Unable to convert -3.402823E+38 to Decimal.
//       -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
//
//       -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
//       -67890.13 (Single) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE) (UInt64)
//       -67890.13 (Single) --> -67890.12 (Decimal)
//       -67890.13 (Single) --> -67890.125 (Double)
//
//       -12345.68 (Single) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
//       -12345.68 (Single) --> 18446744073709539271 (0xFFFFFFFFFFFFCFC7) (UInt64)
//       -12345.68 (Single) --> -12345.68 (Decimal)
//       -12345.68 (Single) --> -12345.6787109375 (Double)
//
//       12345.68 (Single) --> 12345 (0x0000000000003039) (Int64)
//       12345.68 (Single) --> 12345 (0x0000000000003039) (UInt64)
//       12345.68 (Single) --> 12345.68 (Decimal)
//       12345.68 (Single) --> 12345.6787109375 (Double)
//
//       67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
//       67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
//       67890.13 (Single) --> 67890.12 (Decimal)
//       67890.13 (Single) --> 67890.125 (Double)
//
//       3.402823E+38 (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       3.402823E+38 (Single) --> 0 (0x0000000000000000) (UInt64)
//       Unable to convert 3.402823E+38 to Decimal.
//       3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
//
//       NaN (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       NaN (Single) --> 0 (0x0000000000000000) (UInt64)
//       Unable to convert NaN to Decimal.
//       NaN (Single) --> NaN (Double)
//
//       Infinity (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       Infinity (Single) --> 0 (0x0000000000000000) (UInt64)
//       Unable to convert Infinity to Decimal.
//       Infinity (Single) --> Infinity (Double)
//
//       -Infinity (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
//       -Infinity (Single) --> 9223372036854775808 (0x8000000000000000) (UInt64)
//       Unable to convert -Infinity to Decimal.
//       -Infinity (Single) --> -Infinity (Double)
Module Example6
    Public Sub Main()
        Dim values() As Single = {Single.MinValue, -67890.1234, -12345.6789,
                                 12345.6789, 67890.1234, Single.MaxValue,
                                 Single.NaN, Single.PositiveInfinity,
                                 Single.NegativeInfinity}
        For Each value In values
            Try
                Dim lValue As Long = CLng(value)
                Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
                               value, value.GetType().Name,
                               lValue, lValue.GetType().Name)
            Catch e As OverflowException
                Console.WriteLine("Unable to convert {0} to Int64.", value)
            End Try
            Try
                Dim ulValue As UInt64 = CULng(value)
                Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
                               value, value.GetType().Name,
                               ulValue, ulValue.GetType().Name)
            Catch e As OverflowException
                Console.WriteLine("Unable to convert {0} to UInt64.", value)
            End Try
            Try
                Dim dValue As Decimal = CDec(value)
                Console.WriteLine("{0} ({1}) --> {2} ({3})",
                               value, value.GetType().Name,
                               dValue, dValue.GetType().Name)
            Catch e As OverflowException
                Console.WriteLine("Unable to convert {0} to Decimal.", value)
            End Try

            Dim dblValue As Double = value
            Console.WriteLine("{0} ({1}) --> {2} ({3})",
                           value, value.GetType().Name,
                           dblValue, dblValue.GetType().Name)
            Console.WriteLine()
        Next
    End Sub
End Module
' The example displays the following output for conversions performed
' in a checked context:
'       Unable to convert -3.402823E+38 to Int64.
'       Unable to convert -3.402823E+38 to UInt64.
'       Unable to convert -3.402823E+38 to Decimal.
'       -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
'
'       -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
'       Unable to convert -67890.13 to UInt64.
'       -67890.13 (Single) --> -67890.12 (Decimal)
'       -67890.13 (Single) --> -67890.125 (Double)
'
'       -12345.68 (Single) --> -12346 (0xFFFFFFFFFFFFCFC6) (Int64)
'       Unable to convert -12345.68 to UInt64.
'       -12345.68 (Single) --> -12345.68 (Decimal)
'       -12345.68 (Single) --> -12345.6787109375 (Double)
'
'       12345.68 (Single) --> 12346 (0x000000000000303A) (Int64)
'       12345.68 (Single) --> 12346 (0x000000000000303A) (UInt64)
'       12345.68 (Single) --> 12345.68 (Decimal)
'       12345.68 (Single) --> 12345.6787109375 (Double)
'
'       67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
'       67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
'       67890.13 (Single) --> 67890.12 (Decimal)
'       67890.13 (Single) --> 67890.125 (Double)
'
'       Unable to convert 3.402823E+38 to Int64.
'       Unable to convert 3.402823E+38 to UInt64.
'       Unable to convert 3.402823E+38 to Decimal.
'       3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
'
'       Unable to convert NaN to Int64.
'       Unable to convert NaN to UInt64.
'       Unable to convert NaN to Decimal.
'       NaN (Single) --> NaN (Double)
'
'       Unable to convert Infinity to Int64.
'       Unable to convert Infinity to UInt64.
'       Unable to convert Infinity to Decimal.
'       Infinity (Single) --> Infinity (Double)
'
'       Unable to convert -Infinity to Int64.
'       Unable to convert -Infinity to UInt64.
'       Unable to convert -Infinity to Decimal.
'       -Infinity (Single) --> -Infinity (Double)
' The example displays the following output for conversions performed
' in an unchecked context:
'       -3.402823E+38 (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
'       -3.402823E+38 (Single) --> 9223372036854775808 (0x8000000000000000) (UInt64)
'       Unable to convert -3.402823E+38 to Decimal.
'       -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
'
'       -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
'       -67890.13 (Single) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE) (UInt64)
'       -67890.13 (Single) --> -67890.12 (Decimal)
'       -67890.13 (Single) --> -67890.125 (Double)
'
'       -12345.68 (Single) --> -12346 (0xFFFFFFFFFFFFCFC6) (Int64)
'       -12345.68 (Single) --> 18446744073709539270 (0xFFFFFFFFFFFFCFC6) (UInt64)
'       -12345.68 (Single) --> -12345.68 (Decimal)
'       -12345.68 (Single) --> -12345.6787109375 (Double)
'
'       12345.68 (Single) --> 12346 (0x000000000000303A) (Int64)
'       12345.68 (Single) --> 12346 (0x000000000000303A) (UInt64)
'       12345.68 (Single) --> 12345.68 (Decimal)
'       12345.68 (Single) --> 12345.6787109375 (Double)
'
'       67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
'       67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
'       67890.13 (Single) --> 67890.12 (Decimal)
'       67890.13 (Single) --> 67890.125 (Double)
'
'       3.402823E+38 (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
'       3.402823E+38 (Single) --> 0 (0x0000000000000000) (UInt64)
'       Unable to convert 3.402823E+38 to Decimal.
'       3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
'
'       NaN (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
'       NaN (Single) --> 0 (0x0000000000000000) (UInt64)
'       Unable to convert NaN to Decimal.
'       NaN (Single) --> NaN (Double)
'
'       Infinity (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
'       Infinity (Single) --> 0 (0x0000000000000000) (UInt64)
'       Unable to convert Infinity to Decimal.
'       Infinity (Single) --> Infinity (Double)
'
'       -Infinity (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
'       -Infinity (Single) --> 9223372036854775808 (0x8000000000000000) (UInt64)
'       Unable to convert -Infinity to Decimal.
'       -Infinity (Single) --> -Infinity (Double)

Para 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 Single estructura y los tipos relacionados proporcionan métodos para realizar las siguientes categorías de operaciones:

  • Comparación de valores. Puede llamar al Equals método para determinar si dos Single valores son iguales o el CompareTo método para determinar la relación entre dos valores.

    La Single 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 , Doubleel Single valor se convierte en un Double antes de realizar la comparación. Si uno de los operandos es un tipo entero, se convierte en un Single antes de realizar la comparación. Aunque estas son conversiones de ampliación, pueden implicar una pérdida de precisión.

    Advertencia

    Debido a las diferencias de precisión, dos Single 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 Single 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 de Lenguaje intermedio común (CIL) en lugar de por Single métodos. Si el otro operando de una operación matemática es , Doubleel Single se convierte en un Double antes de realizar la operación y el resultado de la operación también es un Double valor. Si el otro operando es un tipo entero, se convierte en un Single antes de realizar la operación y el resultado de la operación también es un Single valor.

    Puede realizar otras operaciones matemáticas llamando a static (Shared en Visual Basic) métodos de la System.Math clase . Estos incluyen métodos adicionales que se usan habitualmente para la aritmética (como , y ), geometry (como Math.Cos y Math.Sin) y cálculo (como Math.Log).Math.SqrtMath.SignMath.Abs En todos los casos, el Single valor se convierte en .Double

    También puede manipular los bits individuales de un Single valor. El BitConverter.GetBytes(Single) método devuelve su patrón de bits en una matriz de bytes. Al pasar esa matriz de bytes al BitConverter.ToInt32 método , también puede conservar el Single patrón de bits del valor en un entero de 32 bits.

  • 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 Single valor llamando al Math.Round método . Sin embargo, tenga en cuenta que el Single valor se convierte en un Double antes de llamar al método y la conversión puede implicar una pérdida de precisión.

  • Formato. Puede convertir un Single 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 Single 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 Single 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 para todos los demás tipos numéricos estándar, excepto la conversión de Double a Single valores. La conversión de un valor de cualquier tipo numérico estándar distinto de a Double a Single es una conversión de ampliación y no requiere el uso de un operador de conversión o método de conversión.

    Sin embargo, la conversión de valores enteros de 32 y 64 bits puede implicar una pérdida de precisión. En la tabla siguiente se enumeran las diferencias de precisión de 32 bits, 64 bits y Double tipos:

    Tipo Precisión máxima (en dígitos decimales) Precisión interna (en dígitos decimales)
    Double 15 17
    Int32 y UInt32 10 10
    Int64 y UInt64 19 19
    Single 7 9

    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, ya que uno de los valores es un valor de punto flotante de precisión sencilla que se convierte en .Double

    using System;
    
    public class Example8
    {
        public static void Main()
        {
            Double value1 = 1 / 3.0;
            Single sValue2 = 1 / 3.0f;
            Double value2 = (Double)sValue2;
            Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,
                                                value1.Equals(value2));
        }
    }
    // The example displays the following output:
    //        0.33333333333333331 = 0.3333333432674408: False
    
    let value1 = 1. / 3.
    let sValue2 = 1f / 3f
    let value2 = double sValue2
    printfn $"{value1:R} = {value2:R}: {value1.Equals value2}"
    // The example displays the following output:
    //        0.33333333333333331 = 0.3333333432674408: False
    
    Module Example9
        Public Sub Main()
            Dim value1 As Double = 1 / 3
            Dim sValue2 As Single = 1 / 3
            Dim value2 As Double = CDbl(sValue2)
            Console.WriteLine("{0} = {1}: {2}", value1, value2, value1.Equals(value2))
        End Sub
    End Module
    ' The example displays the following output:
    '       0.33333333333333331 = 0.3333333432674408: False