我使用的是Visual Studio Professional 2012。我创建了一个新的c# ConsoleApplication,目标是。net Framework 4.5,代码如下:
static void Main(string[] args)
{
double x = 2.44445;
double y = Math.Round(x, 4, MidpointRounding.AwayFromZero);
Console.WriteLine(y);
Console.ReadKey();
}
预期结果应该是2.4445,但它实际返回2.4444。//与以前的框架版本相同,我尝试了VCE2010。
我知道这样的问题通常源于双数据类型的存储方式(即有限小数转换为无限二进制分数)。但我没想到只有5个十进制数字,比如2.44445
我担心这样的事情是否会发生在更短的小数上。我还想在c#中学习一种更安全的舍入方式(使用远离零约定)。谢谢。
这确实是由于浮点数的精度很脆弱。0.5可以很好地存储在IEEE浮点数中,而0.45、0.445等则不能。例如,指定2.44445时存储的实际值是11009049289107177/4503599627370496,即2.4444999999999999999989519494647…现在应该很明显了,为什么这个数字是四舍五入的。
如果需要精确地存储小数,可以考虑使用decimal
类型。
from msdn:
因为表示会导致精度的损失作为浮点数或执行算术的十进制值对浮点值的操作,在某些情况下,Round(Double),Int32, MidpointRounding)方法可能不会出现对中点进行四舍五入由mode参数指定的值。说明了这一点下面的例子中,2.135四舍五入到2.13而不是2.14。发生这种情况是因为方法内部将值乘以在这种情况下,乘法运算会受到a的影响精度损失.
Round是这样实现的:
double num = roundPower10Double[digits];
value *= num;
if (mode == MidpointRounding.AwayFromZero)
{
double num2 = SplitFractionDouble(&value);
if (Abs(num2) >= 0.5)
{
value += Sign(num2);
}
}
可以看到,value乘以num, num是
中的值roundPower10Double = new double[] { 1.0, 10.0, 100.0, 1000.0, 10000.0,
100000.0, 1000000.0, 10000000.0, 100000000.0, 1000000000.0, 10000000000,
100000000000, 1000000000000, 10000000000000, 100000000000000, 1E+15 };
所以实际上你有2.44445 * 10000.0 - 24444,0
得到0,499999999996362
。它比0.5
小。因此,您有2.4444
。