我正在使用Visual Studio Professional 2012。我创建了一个新的C# ConsoleApplication,目标是.NET Framework4.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#中舍入(使用远离零约定)的方法。谢谢。
发布于 2012-12-27 21:45:10
这确实是由于浮点数的脆弱精度造成的。0.5可以完美地存储在IEEE浮点中,但0.45、0.445等不能。例如,指定2.44445时存储的实际值是11009049289107177/4503599627370496,即2.44449999999999989519494647...现在应该很明显为什么数字是这样四舍五入的。
如果需要精确地存储小数,请考虑使用decimal
类型。
发布于 2012-12-27 21:47:00
由于将十进制值表示为浮点数或对浮点值执行算术运算可能会导致精度损失,因此在某些情况下,
(Double,Int32,MidpointRounding)方法可能不会按照模式参数的指定对中点值进行舍入。下面的示例说明了这一点,其中2.135四舍五入为2.13,而不是2.14。这是因为在内部,该方法将值乘以10位数,而本例中的乘法操作会损失精度。
这是如何实现的圆周:
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 };
所以,实际上你有给出0,499999999996362
的2.44445 * 10000.0 - 24444,0
。这比0.5
还小。这样就有了2.4444
。
https://stackoverflow.com/questions/14055611
复制相似问题