我正在使用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#中舍入(使用远离零惯例)。 感谢。
答案 0 :(得分:11)
这确实是由于浮点数的精确度很脆弱。 0.5可以完美地存储在IEEE浮点数中,但0.45,0.445等不能存储。例如,指定2.44445时存储的实际值为11009049289107177/4503599627370496,即2.44449999999999989519494647 ...现在应该很明显为什么数字按原样舍入。
如果您需要精确存储小数,请考虑使用decimal
类型。
答案 1 :(得分:3)
由于代表可能导致精度损失 十进制值作为浮点数或执行算术 对浮点值的操作,在某些情况下为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
。