20

Why is it that when I do the following...

Math.Round(0.75, 1, MidpointRounding.AwayFromZero)

I get 0.8

but when I do the following...

Math.Round(0.575, 2, MidpointRounding.AwayFromZero)

I don't get 0.58. Instead I get 0.57. I want anything that is 5 and up rounding up, so 0.575 should be 0.58.

Vivek S.
  • 19,945
  • 7
  • 68
  • 85
Steve B
  • 557
  • 2
  • 7
  • 23
  • The [first answer in this question](http://stackoverflow.com/q/5641616/62576) should help answer this one. – Ken White Aug 24 '12 at 16:29
  • @KenWhite: I'm not sure it will. The problem is that by real maths rounding `.575` to two decimal places away from zero will give `.58` but this is not the observed result. Its to do with floating poitn representations in this case. – Chris Aug 24 '12 at 16:33
  • @Chris: You're right. :-) I primarily use Delphi, and it's Round() handles things like this just fine for the most part. I just checked in C#, though, and can see the issue. – Ken White Aug 24 '12 at 16:48

3 Answers3

36

The problem will be that you cannot represent 0.575 exactly as a binary floating point number (eg a double). Though I don't know exactly it seems that the representation closest is probably just a bit lower and so when rounding it uses the true representation and rounds down.

If you want to avoid this problem then use a more appropriate data type. decimal will do what you want:

Math.Round(0.575M, 2, MidpointRounding.AwayFromZero)

Result: 0.58

The reason that 0.75 does the right thing is that it is easy to represent in binary floating point since it is simple 1/2 + 1/4 (ie 2^-1 +2^-2). In general any finite sum of powers of two can be represented in binary floating point. Exceptions are when your powers of 2 span too great a range (eg 2^100+2 is not exactly representable).

Edit to add:

Formatting doubles for output in C# might be of interest in terms of understanding why its so hard to understand that 0.575 is not really 0.575. The DoubleConverter in the accepted answer will show that 0.575 as an Exact String is 0.5749999999999999555910790149937383830547332763671875 You can see from this why rounding give 0.57.

Community
  • 1
  • 1
Chris
  • 27,210
  • 6
  • 71
  • 92
  • 2
    Thanks for all the info guys. I switched my double to a decimal and that does the trick. Thanks! – Steve B Aug 24 '12 at 17:03
9

The System.Math.Round method uses the Double structure, which, as others have pointed out, is prone to floating point precision errors. The simple solution I found to this problem when I encountered it was to use the System.Decimal.Round method, which doesn't suffer from the same problem and doesn't require redifining your variables as decimals:

Decimal.Round(0.575, 2, MidpointRounding.AwayFromZero)

Result: 0.58

Antagony
  • 1,750
  • 12
  • 17
2

It is caused by a lack of precision with doubles / decimals (i.e. - the function will not always give the result you expect).

See the following link: MSDN on Math.Round

Here is the relevant quote:

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.

Jason
  • 1,385
  • 9
  • 11