System.Double.Epsilon プロパティ

この記事では、この API のリファレンス ドキュメントへの補足的な解説を提供します。

プロパティの値は、インスタンスの Epsilon 値が 0 の場合に数値演算または比較で有効な最小の正 Double の値を Double 反映します。 たとえば、次のコードは、ゼロが等しくない値と 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 ビット仮数または仮数、および 11 ビット指数で構成されます。 次の例に示すように、ゼロの指数は -1022、仮数は 0 です。 Epsilon には -1022 の指数と 1 の仮数があります。 これは、0 より大きい最小の正Doubleの値であり、指数が -1022 である場合に可能な最小の値と可能な最小の増分Doubleを表しますEpsilon

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 一般的な尺度ではありません。値が 0 または指数 -1022 のインスタンスにのみ Double 適用されます。

Note

このプロパティの Epsilon 値は、浮動小数点演算での丸めによる相対エラーの上限を表す machine epsilon と同じではありません。

この定数の値は 4.94065645841247e-324 です。

2 つの明らかに等価な浮動小数点数は、最下位桁の違いにより等しくない可能性があります。 たとえば、C# 式は、左辺の除算演算は最大有効桁数を持ち、 (double)1/3 == (double)0.33333右側の定数は指定した数字に対してのみ正確であるため、等しく比較されません。 2 つの浮動小数点数を等しいと見なすことができるかどうかを決定するカスタム アルゴリズムを作成する場合は、定数の値に基づいて、2 つの値が等しいと見なされる差の Epsilon 許容される絶対マージンを確立することはお勧めしません。 (通常、差の余白は何倍もEpsilon大きくなります。2 つの倍精度浮動小数点値の比較については、次を参照してくださいDoubleEquals(Double)

プラットフォームに関する注意事項

ARM システムでは、定数の Epsilon 値が小さすぎて検出できないため、0 に相当します。 代わりに、2.2250738585072014E-308 と等しい代替 epsilon 値を定義できます。