.NET Math.Round(<double>,<int>,MidpointRounding.AwayFromZero)无法正常工作</int> </double>

时间:2012-12-27 13:38:24

标签: c#

我正在使用Visual Studio Professional 2012。 我创建了一个针对.NET Framework 4.5的新C#ConsoleApplication,其代码如下:

    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#中舍入(使用远离零惯例)。 感谢。

2 个答案:

答案 0 :(得分:11)

这确实是由于浮点数的精确度很脆弱。 0.5可以完美地存储在IEEE浮点数中,但0.45,0.445等不能存储。例如,指定2.44445时存储的实际值为11009049289107177/4503599627370496,即2.44449999999999989519494647 ...现在应该很明显为什么数字按原样舍入。

如果您需要精确存储小数,请考虑使用decimal类型。

答案 1 :(得分:3)

Notes from msdn

  

由于代表可能导致精度损失   十进制值作为浮点数或执行算术   对浮点值的操作,在某些情况下为Round(Double,   Int32,MidpointRounding)方法可能不会出现圆中点   mode参数指定的值。这在图中说明   下面的示例,其中2.135舍入到2.13而不是2.14。   发生这种情况是因为内部方法将值乘以   10个数字,在这种情况下的乘法运算受到a   精度不足

Round实施的方式如下:

double num = roundPower10Double[digits];
value *= num;
if (mode == MidpointRounding.AwayFromZero)
{
    double num2 = SplitFractionDouble(&value);
    if (Abs(num2) >= 0.5)
    {
        value += Sign(num2);
    }
}

如您所见,值乘以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