System.Double.Epsilon 属性

本文提供了此 API 参考文档的补充说明。

该属性的值 Epsilon 反映当实例的值为零时,数值运算或比较中具有显著意义的最小正 DoubleDouble 。 例如,以下代码显示零,被视为 Epsilon 不相等值,而零和一半的值 Epsilon 被视为相等。

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

更确切地说,浮点格式包括符号、52 位 mantissa 或标志以及 11 位指数。 如以下示例所示,零的指数为 -1022,mantissa 为 0。 Epsilon 指数为 -1022,指数为 1。 这意味着 Epsilon ,是大于零的最小正 Double 值,表示其指数为 -1022 的最小可能值和最小可能增量 Double

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

但是,该 Epsilon 属性不是类型的一般精度 Double 度量值;它仅适用于 Double 值为零或指数为 -1022 的实例。

注意

该属性的值 Epsilon 不等效于计算机 epsilon,它表示由于浮点算术中舍入而导致相对误差的上限。

此常量的值为 4.94065645841247e-324。

由于最小有效位数的差异,两个明显等效的浮点数可能无法相等。 例如,C# 表达式不相等,因为左侧的除法运算具有最大精度, (double)1/3 == (double)0.33333而右侧的常量仅精确到指定的数字。 如果创建自定义算法来确定是否可以将两个浮点数视为相等,则不建议将算法基于常量的值 Epsilon 来建立两个值相等的可接受的绝对差差。 (通常,差异的边距比 Epsilon..有关比较两个双精度浮点值的信息,请参阅 DoubleEquals(Double)

平台说明

在 ARM 系统上,常量的值 Epsilon 太小,无法检测到,因此相当于零。 可以定义一个替代的 epsilon 值,该值等于 2.2250738585072014E-308。