Is MidpointRounding.AwayFromZero working right in .NET Core 3.1?

Viewed 515

It's my understanding from the docs that MidpointRounding.ToEven is the default behavior of Math.Round(). I am using MidpointRounding.AwayFromZero to override that and it does not appear to work. I'm either confused about how MidpointRounding.AwayFromZero works or it isn't working right.

For example, I have a double: 0.285. In my simple mind, when rounding that to two decimal places, that should round to 0.29. However, .NET Core 3.1 Math.Round is rounding it to 0.28, which is exactly the same behavior as the default for Math.Round and also the MidpointRounding.ToEven. Because, 0.29 is further away from zero than 0.28, then one would think that MidpointRounding.AwayFromZero would return 0.29, right? Why name it AwayFromZero and then return a number that is closer to zero. That doesn't make sense.

Math.Round(0.285, 2, MidpointRounding.AwayFromZero) // .NET says this is 0.28
1 Answers

By default, the Math. Round method uses Banker's Rounding not normal rounding.
In banker's rounding a number with a final digit of 5 is rounded to the nearest even number rather than to the next larger number as you might expect. The idea is that statistically half of a sample of numbers are rounded up and half are rounded down.

The reasons with your digital number in normal rounding are described in the office doc.

Math.Round(0.285, 2, MidpointRounding.AwayFromZero) // .NET says this is 0.28

Notes to Callers
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 10digits, and the multiplication operation in this case suffers from a loss of precision.

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. 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.@cdhowie

If you need to store fractional numbers precisely, consider using the decimal type instead.

Solution
using public static decimal Round(decimal d, int decimals, MidpointRounding mode);

Math.Round((decimal)0.285, 2, MidpointRounding.AwayFromZero); // .NET is 0.29

More details about Math.Round you can see answer from @Sergey Berezovskiy.

.NET Math.Round(,,MidpointRounding.AwayFromZero) not working correctly

Related