Why does .NET use banker's rounding as default?
Asked Answered
C

5

296

According to the documentation, the decimal.Round method uses a round-to-even algorithm which is not common for most applications. So I always end up writing a custom function to do the more natural round-half-up algorithm:

public static decimal RoundHalfUp(this decimal d, int decimals)
{
    if (decimals < 0)
    {
        throw new ArgumentException("The decimals must be non-negative", 
            "decimals");
    }

    decimal multiplier = (decimal)Math.Pow(10, decimals);
    decimal number = d * multiplier;

    if (decimal.Truncate(number) < number)
    {
        number += 0.5m;
    }
    return decimal.Round(number) / multiplier;
}

Does anybody know the reason behind this framework design decision?

Is there any built-in implementation of the round-half-up algorithm into the framework? Or maybe some unmanaged Windows API?

It could be misleading for beginners that simply write decimal.Round(2.5m, 0) expecting 3 as a result but getting 2 instead.

Coffeepot answered 22/11, 2008 at 19:51 Comment(13)
> I would expect 72.5, but the result is 72.4 I wouldn't. Are you saying you'd round 2.444444444444444449 up to three? (If you were rounding to zero decimal places)Inca
Rounding up is not "more natural." Nature has nothing to do with it. It's simply what you learned in gradeschool when you learned the concept of "rounding." Gradeschool lessons don't always paint a full picture.Bleach
@Rob And that's why it is more natural, even though it ain't correctPomiculture
I don't understand, @Pacerier. I explained why it's not natural, and you say that's in fact why it is natural. How does my argument work against my conclusion, which is the opposite of yours? Things you've grown accustomed to might feel natural, and sometimes we figuratively say that something is "second nature," but that doesn't make them natural.Bleach
@Rob I'm saying it is natural, because it feels natural. You do know that there are 36 different objects with the same variable name natural right?Pomiculture
nature's defintely analogue so it's the wrong word to use; but this is being pedantic. Maybe 'usual' would be a better word to use.."what is the usual rounding that people do" > 0.5 goes to 1.0Edan
BTW, it is interesting to note that when rounding a double when converting to a string, AwayFromZero rounding is used (i.e. 0.5.ToString("0") == "1"). I think that usually when you round, you do it to display the value on the screen. So this makes it really interesting.Orthman
@MatthijsWessels Yes, I also find that very strange. This is a general "inconsistency" which has been present since the beginning of .NET. Here's an additional example for those who don't know this: If d = 9.825m is a Decimal (exactly representable for sure), then stuff like decimal.Round(d, 2) and Math.Round(d, 2) gives 9.82, while formatting like d.ToString("F2"), d.ToString("N2") and d.ToString("0.00") gives "9.83" (the decimal separator character depending on the culture of the current thread). Not friendly.Gleich
@JeppeStigNielsen The only reasoning behind it that I can think of, is that when rounding a value for display on the screen, you don't need the statistical benefit of bankers rounding. The more "natural feeling" away from zero rounding might be the better option. Hence the to string conversions round like that. When rounding values that have to be used in other calculations, then the statistical benefit of bankers rounding is useful. Exactly the kind of situation where you would use Math.Round.Orthman
Offtopic: Bankers rounding is supposed to be unbiased, which it is in theory. But in practice, there are far more even numbers (in the financial world), which makes this debatableHominy
@Hominy It's true that a lot of prices end with 9's, but that doesn't really produce much of a bias. For example, the price could be with or without tax(es) and such. And of course, larger prices are often not 9'd to the last digits (before the final 5). All in all, in all the financial applications I've been part of so far, banker's rounding works great.Fokine
@Pomiculture : In banking applications, in the vast majority of situarions, you are just doing a vast number of summations. Every summation may potentialy add an error to the result. In order to minimize the error, the algorithm employed should not be biased which, in order words, means that it sometimes should round up, sometimes round down. It does not really matter what makes your brain feel natural. What really matters is the need imposed by a certain use case or requirement. If the bankers' round is arguably the best solution for the problem... well... it is natural to use it!Shooter
@Pomiculture :: By the way, financial applications (written by well skilled developers, I mean) employ fixed point arithmetic, not floating point arithmetic. Employing fixed point arithmetic, all calculations are precise and there's no rounding errors at all.Shooter
T
209

Probably because it's a better algorithm. Over the course of many roundings performed, you will average out that all .5's end up rounding equally up and down. This gives better estimations of actual results if you are for instance, adding a bunch of rounded numbers. I would say that even though it isn't what some may expect, it's probably the more correct thing to do.

Tombac answered 22/11, 2008 at 19:57 Comment(6)
assuming you have an flat distribution of odd and even inputs of courseQuart
+1 for better algorithm, although Ostemar has the actual answer (#312196)Kutz
@Ian, I give that answer +1 as well. Anyway we can get the "accepted answer moved" Maybe the OP can do this. The actual answer to "why" it uses this method is half way down the page. Although I quite like the rep boost I get about once a week from this answer.Tombac
@Tombac - thanks for pushing up my answer. I guess it's up to the OP to change the accepted answer as he sees fit?Sherbet
-1 for stating it's a better algorithm. - Given a random sample of number using banker's rounding you will end up having more numbers at even positions than odd positions. - It is only after you average those numbers that you get again a similar spread to the original distribution. - However if you for example would plot this data in a scatter plot one might see artificial grouping.Electrolyze
For future visitors, Dango's answer (https://mcmap.net/q/41238/-c-rounding-midpointrounding-toeven-vs-midpointrounding-awayfromzero) complements this one well, and provides an excellent example demonstrating how the option MidpointRounding.ToEven can yield more accurate results.Citify
S
458

The other answers with reasons why the Banker's algorithm (aka round half to even) is a good choice are quite correct. It does not suffer from negative or positive bias as much as the round half away from zero method over most reasonable distributions.

But the question was why .NET use Banker's actual rounding as default - and the answer is that Microsoft has followed the IEEE 754 standard. This is also mentioned in MSDN for Math.Round under Remarks.

Also note that .NET supports the alternative method specified by IEEE by providing the MidpointRounding enumeration. They could of course have provided more alternatives to solving ties, but they choose to just fulfill the IEEE standard.

Sherbet answered 3/7, 2011 at 9:9 Comment(8)
So, why does IEEE 754 follow bankers rounding? This (still good) answer just passes the bucket.Wald
@HenkHolterman Probably due to what is mentioned in the other answers (and as I summarized); it does not suffer (too much) from negative or positive bias and as such makes for a more resonable default for most distributions and problem domains.Sherbet
Related: Mathematica.SE: Why round to even integers?Gen
I think it's interesting because IEEE 754 is the standard for floating point numbers of which Decimal is not. Maybe it follows IEEE 754 so that the same algorithm is used for rounding Double as Decimal.Discommon
@BrandonBarkley A Decimal or decimal is a floating point number, and IEEE 754 does include decimal floating point numbers.Angloindian
Interesting. I had not really pondered how decimal numbers were rendered and stored.Discommon
@HenkHolterman : or one could ague that Microsoft passed the bucketAerator
I got here after someone else suggesting the IEEE standard, which I have found includes all 5 definitions of rounding, and the wikipage linked for 754 no longer contains the section referenced, nor does it reference rounding to even, only references the addition of round away from zero.Hunger
T
209

Probably because it's a better algorithm. Over the course of many roundings performed, you will average out that all .5's end up rounding equally up and down. This gives better estimations of actual results if you are for instance, adding a bunch of rounded numbers. I would say that even though it isn't what some may expect, it's probably the more correct thing to do.

Tombac answered 22/11, 2008 at 19:57 Comment(6)
assuming you have an flat distribution of odd and even inputs of courseQuart
+1 for better algorithm, although Ostemar has the actual answer (#312196)Kutz
@Ian, I give that answer +1 as well. Anyway we can get the "accepted answer moved" Maybe the OP can do this. The actual answer to "why" it uses this method is half way down the page. Although I quite like the rep boost I get about once a week from this answer.Tombac
@Tombac - thanks for pushing up my answer. I guess it's up to the OP to change the accepted answer as he sees fit?Sherbet
-1 for stating it's a better algorithm. - Given a random sample of number using banker's rounding you will end up having more numbers at even positions than odd positions. - It is only after you average those numbers that you get again a similar spread to the original distribution. - However if you for example would plot this data in a scatter plot one might see artificial grouping.Electrolyze
For future visitors, Dango's answer (https://mcmap.net/q/41238/-c-rounding-midpointrounding-toeven-vs-midpointrounding-awayfromzero) complements this one well, and provides an excellent example demonstrating how the option MidpointRounding.ToEven can yield more accurate results.Citify
Z
91

While I cannot answer the question of "Why did Microsoft's designers choose this as the default?", I just want to point out that an extra function is unnecessary.

Math.Round allows you to specify a MidpointRounding:

  • ToEven - When a number is halfway between two others, it is rounded toward the nearest even number.
  • AwayFromZero - When a number is halfway between two others, it is rounded toward the nearest number that is away from zero.
Zap answered 22/11, 2008 at 19:59 Comment(8)
And as I've mentioned in related threads, make sure that you're consistent in your rounding - if you sometimes do rounding in the database, and sometimes in .net, you will have strange, one cent errors that will take you weeks to figure out.Islaen
A client once paid me over $40,000 to track down an $0.11 rounding error between two numbers that were both just shy of 1 BILLION dollars; the $0.11 was due to a difference in the 8th digit rounding error between a mainframe and SQL Server. Talk about a perfectionist!Slotter
@EJB - i'd probably be a perfectionist if i was dealing with a billion dollars ;-)Unmeaning
@E.J. Brennan: It took you $40k to figure that out? I see problems like this all the time, rounding is cause #1, double/float normalization is cause #2, programmer error #3 - #3 can be immediately set to #1 if there are no predefined test cases. btw can you please put me in contact with your billionare client, i think i could find a few more $40k bugs in his system too! :DSturgeon
@seanxe: Or if you'd seen Office Space. Seriously, whenever you see mysterious, tiny inaccuracies in money, solving the mystery of exactly how they are happening is almost always a good idea. It's possible you'll decide not to fix bugs, but knowing the underlying cause still has value. I bet many people who work with money are happy to notice even tiny inaccuracies.Burgenland
"Why did Microsoft's designers choose this as the default?" They didn't; the IEEE did. See Ostemar's answer: https://mcmap.net/q/40866/-why-does-net-use-banker-39-s-rounding-as-default (sorry for snarky wording, but if I had a nickel for every developer I met who thought that "Microsoft had a bug in their rounding routines" ...)Gingivitis
@Matthieu N.: Maybe his hourly rate is $80k. :)Orangeism
@E.J.Brennan This usually isn't about perfectionism but rather about the (stupid, stupid) laws. I've had to do a similar thing (rounding "errors" related to "cents" being used in card payments but not cash payments). The error made up about $0.1 a year, but took almost a month to properly "fix" (and the customer still calls us once in a while when he gets scared by reports not showing rounded values, and I have to explain once again why that is correct, by logic and law). The fine for having such a $0.1 error when a tax officer comes around? Up to $100k. Most laws are ridiculous :)Fokine
N
23

Decimals are mostly used for money; banker’s rounding is common when working with money. Or you could say.

It is mostly bankers that need the decimal type; therefore it does “banker’s rounding”

Bankers rounding have the advantage that on average you will get the same result if you:

  • round a set of “invoice lines” before adding them up,
  • or add them up then round the total

Rounding before adding up saved a lot of work in the days before computers.

(In the UK when we went decimal banks would not deal with half pence, but for many years there was still a half pence coin and shop often had prices ending in half pence – so lots of rounding)

Nibbs answered 22/1, 2010 at 12:58 Comment(4)
"Decimals are mostly used for money" ... and everything else that isn't an integer.Relativize
@JohnTyree, Not true most of the time a double/float is used when it is not an integer. see #2546067Nibbs
Wow. Ridiculous mistake on my part. Decmials, yes. Decimals, no. For posterity, I agree with the original sentiment here.Relativize
Bankers might like bankers' rounding, but bookkeepers might not be fans of it, they say that 0.005 difference should round lead to round up by 0.01, not depending whether it is odd or even number.Childish
C
0

Use another overload of Round function like this:

decimal.Round(2.5m, 0,MidpointRounding.AwayFromZero)

It will output 3. And if you use

decimal.Round(2.5m, 0,MidpointRounding.ToEven)

you will get banker's rounding.

Callable answered 26/3, 2017 at 14:44 Comment(1)
This doesn't answer the question of why Banker's Rounding was chosen as the default.Blasting

© 2022 - 2024 — McMap. All rights reserved.