System.Double.Epsilon 属性
本文提供了此 API 参考文档的补充说明。
该属性的值 Epsilon 反映当实例的值为零时,数值运算或比较中具有显著意义的最小正 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 位 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..有关比较两个双精度浮点值的信息,请参阅 Double 和 Equals(Double)。
平台说明
在 ARM 系统上,常量的值 Epsilon 太小,无法检测到,因此相当于零。 可以定义一个替代的 epsilon 值,该值等于 2.2250738585072014E-308。