Wednesday, June 01, 2005
Arithmetic Rounding in .Net
Quiz time: what is the value of test after the following code runs:
12.35, right? No - imagine my surprise to find that it returns 12.34!!
It turns out that .Net employs a type of rounding called "banker's rounding". When the rounding logic encounters a midpoint (like the 5 in my example), rather than always rounding up, it rounds to then nearest even number.
Of course, in my situation, I needed the traditional "arithmetic rounding" which always goes up (or away from zero) when a midpoint is encountered. Finding no such feature in .Net, I wrote my own. If you see any bugs in it, let me know!
BTW, .Net 2 provides an option on the Round function to use either type of rounding. See http://msdn2.microsoft.com/library/wxhaazw6(en-us,vs.80).aspx
decimal test = decimal.Round(12.345m, 2);
12.35, right? No - imagine my surprise to find that it returns 12.34!!
It turns out that .Net employs a type of rounding called "banker's rounding". When the rounding logic encounters a midpoint (like the 5 in my example), rather than always rounding up, it rounds to then nearest even number.
Of course, in my situation, I needed the traditional "arithmetic rounding" which always goes up (or away from zero) when a midpoint is encountered. Finding no such feature in .Net, I wrote my own. If you see any bugs in it, let me know!
public static decimal ArithmeticRound (decimal d, int decimals)
{
decimal power = (decimal)Math.Pow(10, decimals);
// Note: converting to positive number before calculating rounding, so that we don't need separate logic for negative number handling
decimal floored = (decimal.Floor((Math.Abs(d) * power)) / power);
decimal result;
if (Math.Abs(d) >= floored + ((decimal)Math.Pow(10, - (decimals + 1)) * 5))
result = floored + (decimal)Math.Pow(10, -(decimals));
else
result = floored;
return result * Math.Sign(d);
}
BTW, .Net 2 provides an option on the Round function to use either type of rounding. See http://msdn2.microsoft.com/library/wxhaazw6(en-us,vs.80).aspx