Math.Round(value) does to-even rounding

Published on Monday, May 26, 2014


Math.Round(value) does to-even rounding

Take this example:

Which results in this:

With the first 5 it rounded down, and with the second 5 it rounded up. "Wait, what?! Why the heck does it do that" has been the question (in not so many words) for a long time… For many people this is surprising. The default behaviour of Math.Round is the do "to-even rounding".

To-even rounding means that when a number is halfway between two others, it is rounded toward the nearest even number. This is a form of "Bankers' Rounding". Whether this form of rounding was ever used by "bankers" is a bit of a mystery; but, theoretically, this type of rounding isn't skewed upwards and has equal distribution upwards and downwards. Presumably when bankers rounded transactions they would roughly have half the transactions round in their favour (either up or down, depending on whether it was your money or their money) and half the transactions would round in the customer's favour. Whether statistically it does that, I don't know.

You can override what Math.Round does with the Math.Round(value, MidpointRounding) overloads. MidpointRounding was added after Math.Round was released (maybe to address the surprise that many had encountered) which then allows you to round towards even (explicitly) or away from zero.

I, of course, can't say explicitly why Math.Round was designed the way it was. With methods on a class, you often have to take into account the responsibility of the whole class to get a better idea of why a particular method does what it does.

If we look at the Math class, among other methods, it includes Ceiling, Floor, and Truncate. Each of these methods rounds up towards positive infinity, rounds down towards negative infinity, and rounds down towards zero; respectively. So, if you look at Math.Round in the context of Ceiling, Floor, and Truncate; then if Round rounded down, it would be very similar to Floor or Truncate—making it less distinct from the other methods (and likely even more confusing: if Round rounded-down like Floor, what's the difference?…).

If you want rounding-down behaviour, you might be better off using Math.Floor, or Math.Truncate. That way it's semantically explicit what you meant and someone won't encounter "Math.Round(value)" and think "did they really mean to do to-even rounding here?".

But, this type of rounding has been around (no pun intended) for a while. VBScript's Round function did the same thing. Math.Round doing what it does by default may simply be to be compatible with older APIs like VBScript's Round function (or that Round needs to differentiate from Floor—which also existed in VBScript).

comments powered by Disqus