[英].NET Math.Round(<double>,<int>,MidpointRounding.AwayFromZero) not working correctly
I am using Visual Studio Professional 2012. I created a new C# ConsoleApplication, targeting .NET Framework 4.5, with following code: 我正在使用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();
}
The expected result should be 2.4445, but it actually returns 2.4444. 预期结果应为2.4445,但实际上返回2.4444。 //Same result with previous framework version, and I tried VCE2010.
//与之前的框架版本相同,我尝试了VCE2010。
I know such problem usually results from the way double data type is stored (ie finite decimals converted to infinite binary fraction). 我知道这种问题通常是由于存储双数据类型的方式(即有限小数转换为无限二进制分数)。 But I didn't expect this to happen with only 5 decimal digits like 2.44445
但我没想到这只会发生,例如2.44445只有5位小数
I'm worrying if such thing could happen with even shorter decimals. 我担心如果这样的事情会发生甚至更短的小数。 I would also like to learn a safer way to round (using away from zero convention) in C#.
我还想学习一种更安全的方法来在C#中舍入(使用远离零惯例)。 Thanks.
谢谢。
This is indeed due to the fragile precision of floating-point numbers. 这确实是由于浮点数的精确度很脆弱。 0.5 can be stored perfectly in IEEE floating point, but 0.45, 0.445 etc. cannot.
0.5可以完美地存储在IEEE浮点数中,但0.45,0.445等不能存储。 For example, the actual value that is stored when you specify 2.44445 is 11009049289107177/4503599627370496 which is 2.44449999999999989519494647... It should now be obvious why the number is rounded the way it is.
例如,指定2.44445时存储的实际值是11009049289107177/4503599627370496,即2.44449999999999989519494647 ...现在应该很明显为什么数字按原样舍入。
If you need to store fractional numbers precisely, consider using the decimal
type instead. 如果需要精确存储小数,请考虑使用
decimal
类型。
Because of the loss of precision that can result from representing decimal values as floating-point numbers or performing arithmetic operations on floating-point values, in some cases the Round(Double, Int32, MidpointRounding) method may not appear to round midpoint values as specified by the mode parameter.
由于将十进制值表示为浮点数或对浮点值执行算术运算可能导致精度损失,因此在某些情况下,Round(Double,Int32,MidpointRounding)方法可能看起来不像指定的那样舍入中点值通过mode参数。 This is illustrated in the following example, where 2.135 is rounded to 2.13 instead of 2.14.
这在以下示例中说明,其中2.135舍入为2.13而不是2.14。 This occurs because internally the method multiplies value by 10digits, and the multiplication operation in this case suffers from a loss of precision .
发生这种情况是因为内部方法将值乘以10digits,并且在这种情况下的乘法运算会受到精度损失的影响 。
This is how Round implemented: 这就是Round实施的方式:
double num = roundPower10Double[digits];
value *= num;
if (mode == MidpointRounding.AwayFromZero)
{
double num2 = SplitFractionDouble(&value);
if (Abs(num2) >= 0.5)
{
value += Sign(num2);
}
}
As you can see, value is multiplied by num, which is a value from 如您所见,value乘以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 };
So, actually you have 2.44445 * 10000.0 - 24444,0
which gives 0,499999999996362
. 所以,实际上你有
2.44445 * 10000.0 - 24444,0
,它给出了0,499999999996362
。 Which is less than 0.5
. 小于
0.5
。 Thus you have 2.4444
. 因此你有
2.4444
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.