295

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.

15
  • 121
    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. Commented Nov 22, 2008 at 20:23
  • 49
    @Rob And that's why it is more natural, even though it ain't correct Commented Oct 18, 2011 at 13:44
  • 15
    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. Commented Oct 18, 2011 at 14:47
  • 17
    @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? Commented Oct 18, 2011 at 17:09
  • 13
    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.0 Commented May 25, 2012 at 16:38

5 Answers 5

462

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.

Sign up to request clarification or add additional context in comments.

8 Comments

So, why does IEEE 754 follow bankers rounding? This (still good) answer just passes the bucket.
@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.
@BrandonBarkley A Decimal or decimal is a floating point number, and IEEE 754 does include decimal floating point numbers.
@HenkHolterman : or one could ague that Microsoft passed the bucket
|
210

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.

2 Comments

assuming you have an flat distribution of odd and even inputs of course
-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.
92

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.

6 Comments

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.
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!
@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! :D
@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.
"Why did Microsoft's designers choose this as the default?" They didn't; the IEEE did. See Ostemar's answer: stackoverflow.com/a/6562018/385844 (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" ...)
|
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)

4 Comments

"Decimals are mostly used for money" ... and everything else that isn't an integer.
@JohnTyree, Not true most of the time a double/float is used when it is not an integer. see stackoverflow.com/questions/2545567/…
Wow. Ridiculous mistake on my part. Decmials, yes. Decimals, no. For posterity, I agree with the original sentiment here.
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.
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.

1 Comment

This doesn't answer the question of why Banker's Rounding was chosen as the default.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.