8
double a = 18.565
return Math.Round(a,2)

..returns 18.57.
For every other number I tried banker's rounding worked as expected, for example Math.Round(2.565,2) returned 2.56.

Any clue why and when that happens? Is it error or am I missing something about banker's rounding?

Thanks..

Charles
  • 48,924
  • 13
  • 96
  • 136
Damir
  • 337
  • 4
  • 15
  • possible duplicate of [Getting different result in Math.Round](http://stackoverflow.com/questions/1606664/getting-different-result-in-math-round) – Joren Sep 23 '10 at 06:15

4 Answers4

16

As Matthew said, 18.565 can't be accurately represented. The actual value used is 18.565000000000001278976924368180334568023681640625 (found using DoubleConverter), which is clearly beyond half-way. Now I've a sneaking feeling that sometimes Math.Round will consider a value which is actually beyond the half-way point, but which is as close to the half-way point as can be accurately represented, as being exactly at that point. However, I haven't seen any documentation describing the situations in which that's applied, and clearly it's not happening in this case. I wouldn't want to rely on it.

Even the rounded value isn't exactly 18.57 of course. It's actually 18.57000000000000028421709430404007434844970703125.

Fundamentally, if you really, really care about representing decimal values accurately, you should be using decimal. That's not just in terms of Math.Round - it goes to every aspect of handling floating point values.

That does give the right value for Math.Round, of course:

decimal m = 18.565m;
Console.WriteLine(Math.Round(m, 2)); // Prints 18.56
Jon Skeet
  • 1,261,211
  • 792
  • 8,724
  • 8,929
  • Hm, well, this was the first thing I thought of, and checked in some online converter (http://babbage.cs.qc.edu/IEEE-754/Decimal.html). Ofcourse, didn't pay much attention to the fact that only first something decimals are visible, all zeros. Eh, thank you :) – Damir Sep 23 '10 at 06:51
5

18.565 can not be exactly represented as a double. Thus, the binary representation is slightly higher, so it rounds up. If you use decimal:

decimal a = 18.565m;
return Math.Round(a,2)

it can be exactly represented, and you won't have this issue.

Matthew Flaschen
  • 255,933
  • 45
  • 489
  • 528
1

My guess is that the FP representation means it isn't actually a trailing 5; the dangers of FP!

This works fine, though:

        decimal a = 18.565M; // <===== decimal
        var s = Math.Round(a, 2);
Marc Gravell
  • 927,783
  • 236
  • 2,422
  • 2,784
0

Double is a floating point value, so maybe if you write it as 18.565, it is actually in memory something like 18.56500000000000000000000000000000001, and hence it is more than the midpoint.

naivists
  • 30,500
  • 5
  • 56
  • 80