Condividi tramite


Proprietà System.Double.Epsilon

Questo articolo fornisce osservazioni supplementari alla documentazione di riferimento per questa API.

Il valore della Epsilon proprietà riflette il valore positivo più Double piccolo significativo nelle operazioni numeriche o nei confronti quando il valore dell'istanza Double è zero. Ad esempio, il codice seguente mostra che zero e Epsilon sono considerati valori diversi, mentre zero e metà del valore di Epsilon sono considerati uguali.

using System;

public class Example
{
   public static void Main()
   {
      double[] values = { 0, Double.Epsilon, Double.Epsilon * .5 };

      for (int ctr = 0; ctr <= values.Length - 2; ctr++)
      {
         for (int ctr2 = ctr + 1; ctr2 <= values.Length - 1; ctr2++)
         {
            Console.WriteLine("{0:r} = {1:r}: {2}",
                              values[ctr], values[ctr2],
                              values[ctr].Equals(values[ctr2]));
         }
         Console.WriteLine();
      }
   }
}
// The example displays the following output:
//       0 = 4.94065645841247E-324: False
//       0 = 0: True
//
//       4.94065645841247E-324 = 0: False
open System

let values = [| 0.; Double.Epsilon; Double.Epsilon * 0.5 |]

for i = 0 to values.Length - 2 do
    for i2 = i + 1 to values.Length - 1 do
        printfn $"{values[i]:r} = {values[i2]:r}: {values[i].Equals values[i2]}"
    printfn ""
// The example displays the following output:
//       0 = 4.94065645841247E-324: False
//       0 = 0: True
//
//       4.94065645841247E-324 = 0: False
Module Example
   Public Sub Main()
      Dim values() As Double = { 0, Double.Epsilon, Double.Epsilon * .5 }
      
      For ctr As Integer = 0 To values.Length - 2
         For ctr2 As Integer = ctr + 1 To values.Length - 1
            Console.WriteLine("{0:r} = {1:r}: {2}", _
                              values(ctr), values(ctr2), _ 
                              values(ctr).Equals(values(ctr2)))
         Next
         Console.WriteLine()
      Next      
   End Sub
End Module
' The example displays the following output:
'       0 = 4.94065645841247E-324: False
'       0 = 0: True
'       
'       4.94065645841247E-324 = 0: False

Più precisamente, il formato a virgola mobile è costituito da un segno, una mantissa o un significando a 52 bit e un esponente a 11 bit. Come illustrato nell'esempio seguente, zero ha un esponente di -1022 e una mantissa di 0. Epsilon ha un esponente di -1022 e una mantissa di 1. Ciò significa che Epsilon è il valore positivo Double più piccolo maggiore di zero e rappresenta il valore più piccolo possibile e il più piccolo incremento possibile per un il Double cui esponente è -1022.

using System;

public class Example1
{
    public static void Main()
    {
        double[] values = { 0.0, Double.Epsilon };
        foreach (var value in values)
        {
            Console.WriteLine(GetComponentParts(value));
            Console.WriteLine();
        }
    }

    private static string GetComponentParts(double value)
    {
        string result = String.Format("{0:R}: ", value);
        int indent = result.Length;

        // Convert the double to an 8-byte array.
        byte[] bytes = BitConverter.GetBytes(value);
        // Get the sign bit (byte 7, bit 7).
        result += String.Format("Sign: {0}\n",
                                (bytes[7] & 0x80) == 0x80 ? "1 (-)" : "0 (+)");

        // Get the exponent (byte 6 bits 4-7 to byte 7, bits 0-6)
        int exponent = (bytes[7] & 0x07F) << 4;
        exponent = exponent | ((bytes[6] & 0xF0) >> 4);
        int adjustment = exponent != 0 ? 1023 : 1022;
        result += String.Format("{0}Exponent: 0x{1:X4} ({1})\n", new String(' ', indent), exponent - adjustment);

        // Get the significand (bits 0-51)
        long significand = ((bytes[6] & 0x0F) << 48);
        significand = significand | ((long)bytes[5] << 40);
        significand = significand | ((long)bytes[4] << 32);
        significand = significand | ((long)bytes[3] << 24);
        significand = significand | ((long)bytes[2] << 16);
        significand = significand | ((long)bytes[1] << 8);
        significand = significand | bytes[0];
        result += String.Format("{0}Mantissa: 0x{1:X13}\n", new String(' ', indent), significand);

        return result;
    }
}
//       // The example displays the following output:
//       0: Sign: 0 (+)
//          Exponent: 0xFFFFFC02 (-1022)
//          Mantissa: 0x0000000000000
//
//
//       4.94065645841247E-324: Sign: 0 (+)
//                              Exponent: 0xFFFFFC02 (-1022)
//                              Mantissa: 0x0000000000001
open System

let getComponentParts (value: double) =
    let result = $"{value:R}: "
    let indent = result.Length

    // Convert the double to an 8-byte array.
    let bytes = BitConverter.GetBytes value
    // Get the sign bit (byte 7, bit 7).
    let result = result + $"""Sign: {if (bytes[7] &&& 0x80uy) = 0x80uy then "1 (-)" else "0 (+)"}\n"""

    // Get the exponent (byte 6 bits 4-7 to byte 7, bits 0-6)
    let exponent = (bytes[7] &&& 0x07Fuy) <<< 4
    let exponent = exponent ||| ((bytes[6] &&& 0xF0uy) >>> 4)
    let adjustment = if exponent <> 0uy then 1022 else 1023
    let result = result + $"{String(' ', indent)}Exponent: 0x{int exponent - adjustment:X4} ({int exponent - adjustment})\n"

    // Get the significand (bits 0-51)
    let significand = (bytes[6] &&& 0x0Fuy) <<< 48
    let significand = significand ||| byte (int64 bytes[5] <<< 40)
    let significand = significand ||| byte (int64 bytes[4] <<< 32)
    let significand = significand ||| byte (int64 bytes[3] <<< 24)
    let significand = significand ||| byte (int64 bytes[2] <<< 16)
    let significand = significand ||| byte (int64 bytes[1] <<< 8)
    let significand = significand ||| bytes[0]
    result + $"{String(' ', indent)}Mantissa: 0x{significand:X13}\n"

let values = [| 0.; Double.Epsilon |]
for value in values do
   printfn $"{getComponentParts value}"
   printfn ""


//       // The example displays the following output:
//       0: Sign: 0 (+)
//          Exponent: 0xFFFFFC02 (-1022)
//          Mantissa: 0x0000000000000
//
//
//       4.94065645841247E-324: Sign: 0 (+)
//                              Exponent: 0xFFFFFC02 (-1022)
//                              Mantissa: 0x0000000000001
Module Example1
   Public Sub Main()
      Dim values() As Double = { 0.0, Double.Epsilon }
      For Each value In values
         Console.WriteLine(GetComponentParts(value))
         Console.WriteLine()
      Next
   End Sub

   Private Function GetComponentParts(value As Double) As String
      Dim result As String =  String.Format("{0:R}: ", value)
      Dim indent As Integer =  result.Length

      ' Convert the double to an 8-byte array.
      Dim bytes() As Byte = BitConverter.GetBytes(value)
      ' Get the sign bit (byte 7, bit 7).
      result += String.Format("Sign: {0}{1}",
                              If((bytes(7) And &H80) = &H80, "1 (-)", "0 (+)"),
                              vbCrLf)

      ' Get the exponent (byte 6 bits 4-7 to byte 7, bits 0-6)
      Dim exponent As Integer =  (bytes(7) And &H07F) << 4
      exponent = exponent Or ((bytes(6) And &HF0) >> 4)
      Dim adjustment As Integer = If(exponent <> 0, 1023, 1022)
      result += String.Format("{0}Exponent: 0x{1:X4} ({1}){2}",
                              New String(" "c, indent), exponent - adjustment,
                              vbCrLf)

      ' Get the significand (bits 0-51)
      Dim significand As Long =  ((bytes(6) And &H0F) << 48)
      significand = significand Or (bytes(5) << 40)
      significand = significand Or (bytes(4) << 32)
      significand = significand Or (bytes(3) << 24)
      significand = significand Or (bytes(2) << 16)
      significand = significand Or (bytes(1) << 8)
      significand = significand Or bytes(0)
      result += String.Format("{0}Mantissa: 0x{1:X13}{2}",
                              New String(" "c, indent), significand, vbCrLf)

      Return result
   End Function
End Module
' The example displays the following output:
'       0: Sign: 0 (+)
'          Exponent: 0xFFFFFC02 (-1022)
'          Mantissa: 0x0000000000000
'
'
'       4.94065645841247E-324: Sign: 0 (+)
'                              Exponent: 0xFFFFFC02 (-1022)
'                              Mantissa: 0x0000000000001

Tuttavia, la Epsilon proprietà non è una misura generale della precisione del Double tipo. Si applica solo alle Double istanze con valore zero o esponente di -1022.

Nota

Il valore della Epsilon proprietà non equivale a machine epsilon, che rappresenta il limite superiore dell'errore relativo dovuto all'arrotondamento nell'aritmetica a virgola mobile.

Il valore di questa costante è 4,94065645841247e-324.

Due numeri a virgola mobile apparentemente equivalenti potrebbero non essere confrontati a causa delle differenze nelle cifre meno significative. Ad esempio, l'espressione C#, (double)1/3 == (double)0.33333, non viene confrontata come uguale perché l'operazione di divisione sul lato sinistro ha la massima precisione mentre la costante sul lato destro è precisa solo per le cifre specificate. Se si crea un algoritmo personalizzato che determina se due numeri a virgola mobile possono essere considerati uguali, non è consigliabile basare l'algoritmo sul valore della Epsilon costante per stabilire il margine assoluto accettabile della differenza per i due valori da considerare uguali. In genere, tale margine di differenza è molte volte maggiore di Epsilon. Per informazioni sul confronto di due valori a virgola mobile e precisione doppia, vedere Double e Equals(Double).

Note sulla piattaforma

Nei sistemi ARM, il valore della Epsilon costante è troppo piccolo da rilevare, quindi equivale a zero. È possibile definire un valore di epsilon alternativo uguale a 2,2250738585072014E-308.