none
System.Math.Round unexpected behaviour? Bug?

    Question

  • The following are some results for calling System.Math.Round:

    System.Math.Round(2.255d, 2)
    2.26

    System.Math.Round(0.255d, 2)
    0.26

    System.Math.Round(3.255d, 2)
    3.26

    System.Math.Round(4.255d, 2)
    4.26

    System.Math.Round(5.255d, 2)
    5.26

    System.Math.Round(1.255d, 2)
    1.25

    Shouldn't that last value be 1.26 in line with the previous results and the banker's rounding method used by default in the Math.Round method?  Any ideas anyone?

    This behaviour was found in both, the 1.1 and 2.0 frameworks.

    Thursday, June 01, 2006 4:36 PM

Answers

All replies

  • Welcome to computer floating-point math...  See What Every Computer Scientist Should Know About Floating-Point Arithmetic.

    What you're encountering is rounding error.  What is happening is 1.255d is multiplied by 100d before Math.Round() is called to round to the next integer.  1.255d multiplied by 100d results in 125.4999999... which, of course rounds down to 125.0d.

    Sunday, June 04, 2006 4:45 AM
  • Thanks for the reply Ritchie.  I had also seen that that was the algorithm to round using reflector - make the number whole, round using a method inside the CLR, then give the number its fractional part again.

    I'm familiar with floating point math, and many of the potential pitfalls (comparing numbers, order of operations, etc); however, I'm still puzzled as to why:

    0.255d * 10d
    2.55

    1.355d * 100d
    135.5

    2.255d * 100d
    225.5

    3.255d * 100d
    325.5

    1.255d * 100d
    125.49999999999999

    Shouldn't all these values have the same number of significant digits in the mantissa prior to the multiplication?  What's so special about 1.255d?

    Sunday, June 04, 2006 4:54 PM
  • So, thinking about it and reading the article you sent, 1.255d is as special as 1/3.

    Just as 1/3 cannot be represent exactly as base 10 fractional number (0.333333...), 1.255d or 1.1d or 1.277d cannot be represented exactly as base 2 fractional numbers when translated into the internal representation of the floating point number.

    This article also helped me understand it better:

    http://www.yoda.arachsys.com/csharp/floatingpoint.html

     

    Sunday, June 04, 2006 5:59 PM
  • Hi,

    sorry to dig out this old thread... but I was wondering what the recommended way to round doubles is to get the "expected" result?
    Like, if I do Math.Round(5.65,1) I'd like to get 5.7 and not  5.6.

    Thanks,

    Tom
    Wednesday, May 09, 2007 5:28 PM
  • The default for Math.Round is banker's rounding.  In .NET 2.0 they added the ability to choose what type of rounding you require.  If you want different rounding then you can use a member of the MidpointRounding enum.  In the case of 5.65 to 5.7 you'd have to use MidpointRounding.ToEven, but that would give you a different type of result for 4.65.

     

    If you always want to round up you can use Math.Ceiling after multiplying the number by the factor you want rounded to then dividing the result by the same factor.  For example, if you want to round up 5.65 to the first decimal place: Math.Ceiling(5.65 * 10.0)/10.0.

    Wednesday, May 09, 2007 6:16 PM