3

Ok, I realize it's early on a Sunday so I hope I'm just missing something obvious:

I have this function:

private decimal CashConversion(decimal amount, decimal ratio)
{
    if (ratio == 1) return amount;

    decimal convertedAmount = amount / ratio;
    return Math.Round(convertedAmount, 2);
}

When I call it like this:

decimal tax = CashConversion(96.53, 15);

The "tax" variable is equal to 6.43. However, 96.53/15 is 6.435333333333333. Rounding that to 2 places should return 6.44. Am I missing something here?

Scott
  • 13,735
  • 20
  • 94
  • 152
  • similar to [Why does .NET use banker's rounding as default?](http://stackoverflow.com/questions/311696/why-does-net-use-bankers-rounding-as-default) – Eranga Jul 31 '11 at 14:56
  • 4
    There's something you haven't told us about your code, because I get 6.44 when [I try it](http://ideone.com/wHj19) – Ben Voigt Jul 31 '11 at 15:08
  • As Ben Voigt said, this is not the code. For one, it's incorrect (you have to use a `decimal` value for the parameter - you're passing a `double`). And when fixed, it does return `6.44` as is correct. The answer you picked as correct is wrong - banker's rounding applies only to the midpoint, so it would apply to `6.435`, but not to `6.4353`. Please, make sure to get your sample code as close to your actual code as possible, and make sure it does in fact compile, run and display the error you're talking about. You're probably not using `decimal` consistently in your actual code. – Luaan Oct 07 '14 at 09:42
  • Bear in mind that `decimal` is not in fact just a more precise `double` - it's completely different in principle. The reason it's used for financial calculations is because the operations on a `decimal` will mirror the operations that an accountant is going to do - all the rounding errors will be the same as well. – Luaan Oct 07 '14 at 09:46

3 Answers3

5

Check the documentation for Math.Round: As 2 is even, and the next digit after the second is 5, the value is rounded down, according to the IEEE Standard 754, section 4. It's called banker's rounding.

It's not an error, but an expected behaviour. Maybe not the one you were expecting though.

If you want the "mathematically correct" behaviour though, you can call the Decimal.Round(Decimal, Int32, MidpointRounding) overload, as in:

Math.Round(convertedAmount, 2, MidpointRounding.AwayFromZero);
Falanwe
  • 4,636
  • 22
  • 37
  • 1
    What kind of crap is that? LOL. Everyone is taught since early in elementary school that if the next number is a five, its rounded up. I can't be the first person to run into this, is there a way to round in C# that behaves like normal people would expect? – Scott Jul 31 '11 at 14:52
  • Ahh, nevermind. Reading the docs points me to Decimal.Round. Thank you. – Scott Jul 31 '11 at 14:53
  • @Scoot, Math.Round has an overload that takes the MidPointRounding argument. http://msdn.microsoft.com/en-us/library/ms131274.aspx – driis Jul 31 '11 at 14:54
  • I added a link to the overload you're looking for ^^. The default behaviour is to reduce the bias of systematically rounding 5 up. It seems to be a problem for financial applications, so they made a standard to correct the problem. Bankers rejoyce and somewhere a mathematicain is crying. – Falanwe Jul 31 '11 at 14:57
  • The `Math.Round` default rule is quite stupid, actually. There's some ambiguity whether 6.43500000000 should round up or down, because it's equidistant between both, but 6.4353333 is not equidistant, and absolutely should be rounded up. – Ben Voigt Jul 31 '11 at 14:58
  • @Ben Voigt: it is quite stupid if you only consider the mathematical truth. But if you consider the use cases of Math.Round... Most of the time it is used by either financial applications or applications who don't care if the rounding is a little off. Non-finanacial applications that do care usually don't round at all! So it's not that stupid to use the standard of the most common use case. – Falanwe Jul 31 '11 at 15:07
  • @Falanwe: Even in finance, it's wrong. 24.5 cents might make sense to round down, but 24+5/9 should definitely round up. Also, I think you mean it's the Decimal data type that's mostly used by financial applications, rather than `Math.Round` in general. – Ben Voigt Jul 31 '11 at 15:10
  • 2
    @Falanwe there is no truth to consider, bankers rounding is just as true as rounding away from zero, arguably more so as it's less likely to introduce bias – jk. Jul 31 '11 at 22:00
3

That's indeed what is expected: 6.435 would round to 6.44:

When d is exactly halfway between two rounded values, the result is the rounded value that has an even digit in the [(decimals+1)th decimal position]. For example, when rounded to two decimals, the value 2.345 becomes 2.34 and the value 2.355 becomes 2.36[, and 2.3653333 becomes 2.37]. This process is known as rounding toward even, or rounding to nearest.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
  • 6.4353 is still supposed to round up, because 53 > 50. I think the Math.Round function is actually incorrectly specified. – Ben Voigt Jul 31 '11 at 14:56
  • Indeed. I wrote "the [(decimals+1)th decimal position]" in place of "the far right decimal position" because that incorrectly implies that the last decimal place in the number is used for rounding purposes, instead of the first decimal position that will be erased when rounding. – Peter O. Jul 31 '11 at 14:59
2

By default, Math.Round uses banker's rounding. You probably are expecting it to use midpoint rounding. To force this behavior, try this:

Math.Round(convertedAmount, 2, MidpointRounding.AwayFromZero);
Jason
  • 86,222
  • 15
  • 131
  • 146