Math.Floor(double)和Math.Ceiling(double)的意外行为

Eth*_*own 10 c# floating-point

这个问题是关于阈值,Math.Floor(double)Math.Ceiling(double)决定给你前一个或下一个整数值.我不安地发现阈值似乎与之无关Double.Epsilon,这是可用双精度表示的最小值.例如:

double x = 3.0;
Console.WriteLine( Math.Floor( x - Double.Epsilon ) );  // expected 2, got 3
Console.WriteLine( Math.Ceiling( x + Double.Epsilon) ); // expected 4, got 3
Run Code Online (Sandbox Code Playgroud)

即使乘以Double.Epsilon一点点也不能解决问题:

Console.WriteLine( Math.Floor( x - Double.Epsilon*1000 ) );  // expected 2, got 3
Console.WriteLine( Math.Ceiling( x + Double.Epsilon*1000) ); // expected 4, got 3
Run Code Online (Sandbox Code Playgroud)

通过一些实验,我能够确定阈值大约在2.2E-16附近,这非常小,但是比V大得多Double.Epsilon.

出现这个问题的原因是我试图用公式计算数字中的位数var digits = Math.Floor( Math.Log( n, 10 ) ) + 1.这个公式不起作用n=1000(我偶然偶然发现),因为它Math.Log( 1000, 10 )返回的数字比其实际值低4.44E-16.(我后来发现内置Math.Log10(double)提供了更准确的结果.)

不应该将阈值与阈值相关联,Double.Epsilon或者如果不是,则不应该记录阈值(我在MSDN官方文档中找不到任何提及)?

Mar*_*ers 15

不应该将阈值与Double.Epsilon联系起来

没有.

可表示的双精度数不均匀地分布在实数上.接近于零有许多可表示的值.但是从零开始越远,可表示的双打越远.对于非常大的数字,即使将1加到一个双精度也不会给你一个新值.

因此,您要查找的阈值取决于您的数量.这不是一个常数.


Oli*_*rth 10

值为Double.Epsilon4.94065645841247e-324.由于浮点的工作方式,将此值加到3会导致3.

A double有53位尾数,因此您可以添加的最小值会产生任何影响,比您的变量小约2 ^ 53倍.所以1e-16左右的声音听起来是正确的(数量级).

所以回答你的问题:没有"门槛"; floorceil按照你期望的方式简单地对待他们的论点.