Friday, December 17, 2010

Odd Math.Round() behaviour in Mono (same as .NET?)

I had read on a forum somewhere that Math.Round() rounded to the closest even number. I mentioned this to the guys in #mosa and one mentioned that seems like it would be a bug, as it should go from 4.5 -> 5, rather than the (supposedly) expected 4.5 -> 4, with 4 being the closest even number. Another example is 9.5 -> 10 because 10 is the closest even number (9 is odd).

I wrote a simple method to test this.



using System;

namespace round_test
{
class MainClass
{
public static void Main (string[] args)
{
double x = 0d;

while (x < 5d)
{
Console.WriteLine("actual: " + x.ToString());
Console.WriteLine("rounded: " + Math.Round(x).ToString());

x += 0.1d;
}
}
}
}


The output using mono wasn't very consistent. For instance, 2.5 -> 3 while the rest of the n.5 -> closest even number to n (4.5 -> 4, 3.5 -> 4). (full output here). Odd behaviour, bug??


EDIT: It seems there are two types of rounding, explained on MSDN. The default is banker's rounding.

8 comments:

  1. It's not a bug, that kind of rounding is called Banker's Rounding

    ReplyDelete
  2. Yes, after reading the MSDN article I understood what was up. It's still odd though they would pick that as the default. I wonder why that is...

    ReplyDelete
  3. It reduces systematic rounding error in large calculations, because it is not biased toward rounding up. For similar reasons it's the default behavior for IEEE floating point (when one must round to a machine-representable number). -- Different anonymous poster.

    ReplyDelete
  4. Dude, its not even just bankers rounding, its totally normal mathematical rounding. Just think of the numbers starting from .0 to .9, then .5 is actually closer to the next bigger number... if i have to spell it out: 0,1,2,3,4 = round down ; 5,6,7,8,9 round up... it does make sense... just think about it

    ReplyDelete
  5. Also, when you are adding 0.1d, bear in mind that it can't be expressed as an exact floating point number (0.1 in decimal == 0.0001100110011... in binary). So it will be a number slightly less than 0.1 which you are adding. This should explain why 2.5 rounded to 3 - it wasn't actually 2.5.

    When you are doing these things, increment by powers of two (like 0.5 or 0.25) so you don't get errors like this.

    ReplyDelete
  6. The article is confusing, I think because there is some information missing from the first sentence:

    "I had read on a forum somewhere that Math.Round() rounded to the closest even number."

    I think what you mean is:

    "...for cases where the floating point value ends with .5."

    ReplyDelete
  7. http://en.wikipedia.org/wiki/Rounding#Round_half_to_even

    Not a bug. Fixing it would be.

    ReplyDelete