Home > OS >  Does Math.Round in .NET round certain values incorrectly?
Does Math.Round in .NET round certain values incorrectly?

Time:05-24

For example,

double a = 2.55;
double b = Math.Round(a, 1); // expected result is 2.5
Console.WriteLine(b); // 2.6

The reason we expect 2.5 there is that the closest 64-bit IEEE 754 float to 2.55 is exactly 2.54999999999999982236431605997495353221893310546875. Also, specifying MidpointRounding.ToEven or MidpointRounding.AwayFromZero doesn't make a difference here.

On the other hand, the "F" format specifier seems to handle the rounding correctly.

double a = 2.55;
Console.WriteLine($"{a:F1}"); // 2.5

CodePudding user response:

The answer to your actual question "Does Math.Round in .NET round certain values incorrectly?" is: Yes. (Well, Microsoft would probably argue that this behaviour is defined, and is therefore correct.)

The reason for this is described in the documentation for Math.Round():

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. This is illustrated in the following example, where 2.135 is rounded to 2.13 instead of 2.14. This occurs because internally the method multiplies value by 10^digits, and the multiplication operation in this case suffers from a loss of precision.

We can test this:

double a = 2.55;
double c = a * Math.Pow(10, 1); // "a * 10.0" gives the same result.

Console.WriteLine(a.ToString("f16"));
Console.WriteLine(c.ToString("f16"));

The output is:

 2.5499999999999998
25.5000000000000000

You can see that the value after multiplication by 10^1 is 25.5, which will be rounded up in the next step of the rounding algorithm.

You can look at the actual implementation here.

It's a bit fiddly, but the answer is really "something something rounding something" ;)

CodePudding user response:

The default implementation for rounding in .NET is "Round half to even" which is "Bankers Rounding". This means that mid-point values are rounded towards the nearest even number.

CodePudding user response:

Math.Round(value,decimal) works as expected. You define your value as 2.55 and thought you are rounding 2.55 aka 2.54999999... in IEEE 754 but this is false. Rounding with decimal apply a power of 10 to the rounding. So your 2.55 with 1 decimal is apply a single power of 10 so it become 25.5 which is perfectly represented as 25.5 in IEEE 754. Then a rounding become 26.0 then it divide by the factor back to 2.6 hence your results.

CodePudding user response:

Because 2.55 is a midway value, it uses the convention when rounding. Default this is "To Even". So in this case it rounds to the closest even, 2.6. If you use AwayFromZero it will do the same in this case, if you rounded 2.65 it would give different results.

Strange, I get the same result with the string formatter... ? Maybe it's a cultural setting. See also Convert Double to String: Culture specific

  • Related