.NET Math.Round (<double>, <int>, MidpointRounding.AwayFromZero) does not work correctly
I am using Visual Studio Professional 2012. I created a new C # ConsoleApplication targeting the .NET Framework 4.5 with the following code:
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. // The same result with the previous version of the frame, and I tried VCE2010.
I know that this kind of problem usually arises because a double data type is stored (i.e. trailing decimal numbers converted to infinite binary fraction). But I did not expect this to happen with only 5 decimal digits, such as 2.44445
I am worried if such a thing could happen with shorter decimal places. I would also like to know a safer way to round (use away from zero) in C #. Thanks.
This is really due to the fragile precision of floating point numbers. 0.5 can be perfectly stored in the IEEE floating point, but 0.45, 0.445, etc. Can not. For example, the actual value that is stored when 2.44445 is specified is 11009049289107177/4503599627370496, which is 2.44449999999999989519494647 ... Now it should be obvious why the number is rounded as it is.
If you need to store fractional numbers exactly, use the decimal type instead.
Due to the loss of accuracy that may occur as a result of representing decimal values as floating point numbers or performing arithmetic operations with floating point values, in some cases Round (Double, Int32, MidpointRounding) may not be displayed at the midpoint of the value, set by the mode parameter. This is illustrated in the following example, where 2.135 is rounded to 2.13 instead of 2.14. This is because inside the method multiplies the value by 10digits, and the multiplication operation in this case suffers from a loss of accuracy .
Here's how Round works:
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, the value is multiplied by num, which is the value from
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, in fact, you have 2.44445 * 10000.0 - 24444,0 , which gives 0,499999999996362 . It is less than 0.5 . So you have 2.4444 .