在C#中,我该在哪里找到机器精度?

15

机器epsilon通常定义为加一后与一不同的最小数字。

虽然有Double.Epsilon,但名称非常误导:它是可表示的最小(非规范化)Double值,因此对于任何类型的数值编程都没有用。

我想获取Double类型的真正epsilon,以便不必在程序中硬编码公差。我该怎么做?


相关:http://www.johndcook.com/blog/2010/06/08/c-math-gotchas/ - AakashM
@AakashM:我看过了。在IEEE754的视角下,epsilon的含义相当清晰,微软做出这种业余的事情真是可惜。他们的浮点数实现值得信赖吗? - Alexandre C.
@AlexandreC.:Double.MinValue 很可能是定义为与 .NET Framework 中的其他 MinValue 字段相对应,例如 Int32.MinValueDateTime.MinValue 等。这显然与 C 中的 DBL_MIN 不同。但是,我同意 Double.Epsilon 的定义令人困惑。 - Martin Liversage
5个回答

9

在我的机器上:

   1.11022302462516E-16

你可以轻松地计算它:
        double machEps = 1.0d;

        do {
           machEps /= 2.0d;
        }
        while ((double)(1.0 + machEps) != 1.0);

        Console.WriteLine( "Calculated machine epsilon: " + machEps );

已修改:

我计算了 2 倍的 epsilon,现在应该正确了。


@Meonester,您的意思是每次都将machEps除以4(一次在主体中,一次在条件中)吗?如果我省略条件除法(即while ((double)(1.0 + (machEps)) != 1.0);),则machEps的值为1.11022302462516E-16。 - AlanT
@AlanT 没错,这与 Math.NET 代码所示的相符。 - Christian.K
是的,你说得对:Epsilon = 1.11022302462516E-16。应该是: while ((double)(1.0 + (machEps)) != 1.0); - Meonester
@Meonester 如果你已经在进行中,那么你应该相应地编辑你的答案。 - Christian.K
2
第一次不是对的吗?当循环终止时,(1.0 + machEps) == 1.0,这不是我们想要的。我们想要最后一个值,使得(1.0 + machEps) != 1.0 - Rup
显示剩余2条评论

8

Math.NET库定义了一个Precision类,其中有一个DoubleMachineEpsilon属性。

您可以查看它们的实现方式。

根据它的解释,DoubleMachineEpsilon是:

    /// <summary>
    /// The base number for binary values
    /// </summary>
    private const int BinaryBaseNumber = 2;

    /// <summary>
    /// The number of binary digits used to represent the binary number for a double precision floating
    /// point value. i.e. there are this many digits used to represent the
    /// actual number, where in a number as: 0.134556 * 10^5 the digits are 0.134556 and the exponent is 5.
    /// </summary>
    private const int DoublePrecision = 53;

    private static readonly double doubleMachinePrecision = Math.Pow(BinaryBaseNumber, -DoublePrecision);

根据这个来源,它是1,11022302462516E-16


这是相当复杂的位操作,需要了解更多关于 System.Double 的内部表示方式,而我还没有准备好去处理它(这不仅仅是 IEEE754 的问题,还涉及字节序等问题)。 - Alexandre C.
@Alexandre:我敢肯定IEEE-754规范本身就要求使用内部二进制表示法。这里有一个关于“double”的内部机制的良好描述链接。同时,还有Jon的DoubleConverter.cs类的链接;查看它可能会提供一些线索。 - LukeH
@LukeH:现在我注意到微软甚至不能正确使用最基本的术语,我不能假设他们的浮点实现对所有目的都是正确的。 - Alexandre C.
1
浮点数的大部分实现都是硬件实现,所以我怀疑没有太多需要担心的。无论如何,在Double.Epsilon的文档页面上都没有提到“机器epsilon”,这才是你想要的正确术语,所以我不确定它是否令人困惑。 - Rup
2
@Alexandre, @Rup:...而IEEE-754根本没有定义“机器epsilon”,所以double.Epsilon和机器epsilon不同这个事实,与Microsoft的FP实现是否正确完全无关。 - LukeH

2

直接硬编码数值:

const double e1 = 2.2204460492503131e-16;

或者使用二的幂次方:

static readonly double e2 = Math.Pow(2, -52);

或者使用您自己的定义(或多或少):
static readonly double e3 = BitConverter.Int64BitsToDouble(BitConverter.DoubleToInt64Bits(1.0) + 1L) - 1.0;

请查看维基百科:机器精度了解更多信息。


0

LAPACK + DLAMCH,64位英特尔处理器,C#:

var pp = double.Epsilon; // pp = 4.94065645841247E-324
double p = NativeMethods.MachinePrecision('S'); // =DLAMCH('S') 
p = 2.2250738585072014E-308
double.MinValue = -1.7976931348623157E+308
double.MaxValue =  1.7976931348623157E+308

-2

关于Meonester的程序: 实际上,在do ... while循环结束时,machEps的值是这样的:1+machEps == 1。 要获取机器精度,我们必须回到先前的值,在循环后添加以下内容: machEps *= 2.0D; 这将返回2.2204460492503131e-16,与Microsoft文档中Double.Epsilon的建议一致。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接