System.Single.Epsilon 属性

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

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

using System;

public class Example1
{
   public static void Main()
   {
      float[] values = { 0f, Single.Epsilon, Single.Epsilon * .5f };
      
      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 = 1.401298E-45: False
//       0 = 0: True
//       
//       1.401298E-45 = 0: False
open System

let values = [ 0f; Single.Epsilon; Single.Epsilon * 0.5f ]

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 = 1.401298E-45: False
//       0 = 0: True
//       
//       1.401298E-45 = 0: False
Module Example1
    Public Sub Main()
        Dim values() As Single = {0, Single.Epsilon, Single.Epsilon * 0.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 = 1.401298E-45: False
'       0 = 0: True
'       
'       1.401298E-45 = 0: False

更确切地说,单精度浮点格式包括一个符号、一个 23 位 mantissa 或 significand,以及一个 8 位指数。 如以下示例所示,零的指数为 -126,mantissa 为 0。 Epsilon 指数为 -126,指数为 1。 这意味着 Single.Epsilon ,是大于零的最小正 Single 值,表示其指数为 -126 的最小可能值和最小可能增量 Single

using System;

public class Example2
{
   public static void Main()
   {
      float[] values = { 0.0f, Single.Epsilon };
      foreach (var value in values) {
         Console.WriteLine(GetComponentParts(value));
         Console.WriteLine();
      }   
   }

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

      // Convert the single to a 4-byte array.
      byte[] bytes = BitConverter.GetBytes(value);
      int formattedSingle = BitConverter.ToInt32(bytes, 0);
      
      // Get the sign bit (byte 3, bit 7).
      result += String.Format("Sign: {0}\n", 
                              (formattedSingle >> 31) != 0 ? "1 (-)" : "0 (+)");

      // Get the exponent (byte 2 bit 7 to byte 3, bits 6)
      int exponent =  (formattedSingle >> 23) & 0x000000FF;
      int adjustment = (exponent != 0) ? 127 : 126;
      result += String.Format("{0}Exponent: 0x{1:X4} ({1})\n", new String(' ', indent), exponent - adjustment);

      // Get the significand (bits 0-22)
      long significand = exponent != 0 ? 
                         ((formattedSingle & 0x007FFFFF) | 0x800000) : 
                         (formattedSingle & 0x007FFFFF); 
      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: 0xFFFFFF82 (-126)
//          Mantissa: 0x0000000000000
//       
//       
//       1.401298E-45: Sign: 0 (+)
//                     Exponent: 0xFFFFFF82 (-126)
//                     Mantissa: 0x0000000000001
open System

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

    // Convert the single to a 4-byte array.
    let bytes = BitConverter.GetBytes value
    let formattedSingle = BitConverter.ToInt32(bytes, 0)
    
    // Get the sign bit (byte 3, bit 7).
    let result = result + $"""Sign: {if formattedSingle >>> 31 <> 0 then "1 (-)" else "0 (+)"}\n""" 

    // Get the exponent (byte 2 bit 7 to byte 3, bits 6)
    let exponent =  (formattedSingle >>> 23) &&& 0x000000FF
    let adjustment = if exponent <> 0 then 127 else 126
    let result = result + $"{String(' ', indent)}Exponent: 0x{1:X4} ({exponent - adjustment})\n"

    // Get the significand (bits 0-22)
    let significand = 
        if exponent <> 0 then 
            (formattedSingle &&& 0x007FFFFF) ||| 0x800000
        else 
            formattedSingle &&& 0x007FFFFF
             
    result + $"{String(' ', indent)}Mantissa: 0x{significand:X13}\n"


let values = [ 0f; Single.Epsilon ]
for value in values do
    printfn $"{getComponentParts value}\n"
//       // The example displays the following output:
//       0: Sign: 0 (+)
//          Exponent: 0xFFFFFF82 (-126)
//          Mantissa: 0x0000000000000
//       
//       
//       1.401298E-45: Sign: 0 (+)
//                     Exponent: 0xFFFFFF82 (-126)
//                     Mantissa: 0x0000000000001
Module Example2
    Public Sub Main()
        Dim values() As Single = {0.0, Single.Epsilon}
        For Each value In values
            Console.WriteLine(GetComponentParts(value))
            Console.WriteLine()
        Next
    End Sub

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

        ' Convert the single to an 8-byte array.
        Dim bytes() As Byte = BitConverter.GetBytes(value)
        Dim formattedSingle As Integer = BitConverter.ToInt32(bytes, 0)

        ' Get the sign bit (byte 3, bit 7).
        result += String.Format("Sign: {0}{1}",
                              If(formattedSingle >> 31 <> 0, "1 (-)", "0 (+)"),
                              vbCrLf)

        ' Get the exponent (byte 2 bit 7 to byte 3, bits 6)
        Dim exponent As Integer = (formattedSingle >> 23) And &HFF
        Dim adjustment As Integer = If(exponent <> 0, 127, 126)
        result += String.Format("{0}Exponent: 0x{1:X4} ({1}){2}",
                              New String(" "c, indent), exponent - adjustment,
                              vbCrLf)

        ' Get the significand (bits 0-22)
        Dim significand As Long = If(exponent <> 0,
                         (formattedSingle And &H7FFFFF) Or &H800000,
                         formattedSingle And &H7FFFFF)
        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: 0xFFFFFF82 (-126)
'          Mantissa: 0x0000000000000
'       
'       
'       1.401298E-45: Sign: 0 (+)
'                     Exponent: 0xFFFFFF82 (-126)
'                     Mantissa: 0x0000000000001

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

注意

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

此常量的值为 1.4e-45。

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

平台说明

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