tags:

views:

10293

answers:

10

In C#, the result of Math.Round(2.5) is 2.

It is supposed to be 3, isn't it? Is this a C# bug or something?

Thanks!

+140  A: 

Firstly, this wouldn't be a C# bug anyway - it would be a .NET bug. C# is the language - it doesn't decide how Math.Round is implemented.

And secondly, no - if you read the docs, you'll see that the default rounding is "round to even" (banker's rounding):

Return Value
Type: System.Double
The integer nearest a. If the fractional component of a is halfway between two integers, one of which is even and the other odd, then the even number is returned. Note that this method returns a Double instead of an integral type.

Remarks
The behavior of this method follows IEEE Standard 754, section 4. This kind of rounding is sometimes called rounding to nearest, or banker's rounding. It minimizes rounding errors that result from consistently rounding a midpoint value in a single direction.

You can specify how Math.Round should round mid-points using an overload which takes a MidpointRounding value. There's one overload with a MidpointRounding corresponding to each of the overloads which doesn't have one:

Whether this default was well chosen or not is a different matter. (MidpointRounding was only introduced in .NET 2.0. Before then I'm not sure there was any easy way of implementing the desired behaviour without doing it yourself.) In particular, history has shown that it's not the expected behaviour - and in most cases that's a cardinal sin in API design. I can see why Banker's Rounding is useful... but it's still a surprise to many.

You may be interested to take a look at the nearest Java equivalent enum (RoundingMode) which offers even more options. (It doesn't just deal with midpoints.)

Jon Skeet
Awww, Skeet-Owned.
KingNestor
i dont know if this is a bug, i think it was by design since .5 is as close to the nearest lowest integer as it is to the nearest highest integer.
Stan R.
Do you have six hands or something? How do you answer that fast?
Matthew Jones
sorry I didnt see that you said that with sarcasm..my mistake.
Stan R.
I really can't compete with Jon (I shouldn't be competing at all, but that's another story). Given that I'm -8 hours from him, I should really wait until the evenings to reply...
Michael Petrotta
You're assuming I sleep.
Jon Skeet
I knew it! You ARE a robot! :)
Matthew Jones
I remember this behavior in VB before .NET got applied.
John Fiala
Better question: how do you type with boxing gloves on your hands?
Erik Forbes
LOL: "asked 40 mins ago" , "answered 38 mins ago"
bitbonk
Darn it, Jon, you really ARE good. This answer is awesome.
Peter Perháč
And this, ladies and gentlemen, is Jon Skeet's 500th "Nice Answer." Just think about that for a bit.
Michael Myers
@Erik: You're assuming I type with my hands.
Jon Skeet
@bitbonk: Yeah, sorry, I was a bit slow on this one... ;)
Jon Skeet
(Not that mine was the first answer, actually.)
Jon Skeet
may I add that rounding to the nearest even is a part of the floating point standard?
Nefzen
Indeed, IEEE Standard 754, section 4 as the documentation states.
Jon Skeet
I got burned by this a while ago and thought it was sheer lunacy too. Fortunately they added a way to specify the rounding that all of us learned in grade-school; MidPointRounding.
Arnshea
+1 for "it's not the expected behaviour [...] that's a cardinal sin in API design"
BlueRaja - Danny Pflughoeft
Interesting that in Silverlight the overloads of Math.Round with the MidpointRounding parameter don't exist. In fact, the MidpointRounding enum isn't available.
Matt Casto
+21  A: 

From MSDN, Math.Round(double a) returns:

The integer nearest a. If the fractional component of a is halfway between two integers, one of which is even and the other odd, then the even number is returned.

... and so 2.5, being halfway between 2 and 3, is rounded down to the even number (2). this is called Banker's Rounding (or round-to-even), and is a commonly-used rounding standard.

Same MSDN article:

The behavior of this method follows IEEE Standard 754, section 4. This kind of rounding is sometimes called rounding to nearest, or banker's rounding. It minimizes rounding errors that result from consistently rounding a midpoint value in a single direction.

You can specify a different rounding behavior by calling the overloads of Math.Round that take a MidpointRounding mode.

Really, documentation is your friend.

Michael Petrotta
+8  A: 

You should check MSDN for Math.Round:

The behavior of this method follows IEEE Standard 754, section 4. This kind of rounding is sometimes called rounding to nearest, or banker's rounding.

You can specify the behavior of Math.Round using an overload:

Math.Round(2.5, 0, MidpointRounding.AwayFromZero); // gives 3

Math.Round(2.5, 0, MidpointRounding.ToEven); // gives 2
0xA3
Why doesn't AwayFromZero yield 3? Why doesn't ToEven yield 2?
David B
@David B: You are right of course, it's corrected now.
0xA3
+1  A: 

Apparently the round method, when asked to round a number exactly between two integers, returns the even integer. So, Math.Round(3.5) returns 4.

See this article

Matthew Jones
And `Math.Round(4.5)` also returns 4.
Robert Harvey
+2  A: 
Math.Round(2.5, 0, MidpointRounding.AwayFromZero);
Robert Durgin
+1  A: 

How about this:

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

gives the same result as

Math.Round(1123.485, 2, MidpointRounding.ToEven)

and that is 1123.48 :)

How logical is that?

1123.485 is not representable without error in IEEE floating point. Hence it's not a midpoint, so MidpointRounding does not apply.
Yuliy
A: 

This is ugly as all hell, but always produces correct arithmetic rounding.

public double ArithRound(double number,int places){

string numberFormat = "###.";

numberFormat = numberFormat.PadRight(numberFormat.Length + places, '#');

return double.Parse(number.ToString(numberFormat));

}

So does calling `Math.Round` and specifying how you want it to round.
configurator
A: 

Any clues on which way rounding goes when using ToString()? E.g:

decimal a = 1.725m;
uxTB.Text = a.ToString("n");
uxTB.Text = a.ToString("c");
uxTB.Text = a.ToString("0.00");

etc.

I'm in a situation where I'm converting a site from "classic" ASP to ASP.Net and the old VBScript FormayNumber method has been used extensively, which rounds up, and I have been using Math.Round(a, 2) for financial figures until I came upon the "banker's rounding" outcome. The new app must look and work exactly like the old app, so I can't have £1.72 appearing where previously it was £1.73....

I'll be using the full-figured (snigger) decimal amounts (e.g. 1.725) for back-end calculations, and then rounding the outcomes as necessary, as they are financial amounts - I just need to impress this on the project sponsors I think, as they may want me to use Math.Ceiling instead...

EDIT: Why does Math.Round(1.725m, 2) return 1.72? The 2 is clearly between two odd numbers, yet the even number is returned. HAY-YELP!

Ta for any advice...

Mike Kingscott
This isn't the place for question. Ask a new question if you have one: http://stackoverflow.com/questions/ask
Kobi
Math.Round(1.725m, 2) returns 1.72 because the rule is round-to-even for bankers rounding, so 1.725 could round to 1.72 or 1.73, but 1.73 is odd, so 1.72 is selected
JonoW
Thank you JonoW :-)
Mike Kingscott
+3  A: 

This has stung me before with writing reports for accounting, so I'll write a few words of what I found out, previously and from looking into it for this post.

Who are these bankers that are rounding down on even numbers (British bankers perhaps!)?

From wikipedia

The origin of the term bankers' rounding remains more obscure. If this rounding method was ever a standard in banking, the evidence has proved extremely difficult to find. To the contrary, section 2 of the European Commission report The Introduction of the Euro and the Rounding of Currency Amounts suggests that there had previously been no standard approach to rounding in banking; and it specifies that "half-way" amounts should be rounded up.

It seems a very strange way of rounding particularly for banking, unless of course banks use to receive lots of deposits of even amounts. Deposit £2.4m, but we'll call it £2m sir.

The IEEE Standard 754 dates back to 1985 and gives both ways of rounding, but with banker's as the recommended by the standard. This wikipedia article has a long list of how languages implement rounding (correct me if any of the below are wrong) and most use school style rounding:

  • C/C++ round() from math.h rounds away from zero (not banker's rounding)
  • Java Math.Round rounds away from zero (it floors the result, adds 0.5, casts to an integer). There's an alternative in BigDecimal
  • Perl uses a similar way to C
  • Javascript is the same as Java's Math.Round.
Chris S
A: 

Return Value Type: System.Double The integer nearest a. If the fractional component of...

blah blah blah... Who reads this stuff anyway. After stumbling on Math.Round(2.5)==2 bug it took me few seconds to decide to write my own rounding function, and a minute to implement one. But of course I'm still baffled by how so widely used library allows such screw ups. If theoretical math says 2.5 rounded to integral number is 3, then it is. Period. No buts. End of file. :)

PS. value>=0 ? (int)(value+0.5) : -(int)(-value+0.5)

AareP
It gives you a choice. You can either specify how you want the rounding to be done, or you can not care and allow the library to decide by not supplying the parameter. You chose not to care. So it rounded however-the-hell it wanted, and decided to follow the _IEEE_ standard.
configurator
@configurator: there must be thousands of different rounding implementations in c++ libraries (since std doesnt supply one), and I would bet that most of them are not using banker's rounding. So there is IEEE standard and there is industry standard..
AareP
@AareP: "There must be thousands of different rounding implementations" - that's exactly what would happen if the .net libraries hadn't given you the choice. But they do. Did you seriously say "Who reads this stuff anyway"? People who want to know what methods do read this. If you don't want to know, and you don't care what it does, as signified by you not passing the parameters, why do you suddenly care what it does? If you do care what it does, let it know!
configurator
@configurator: Usually I don't care about those auto-generated manuals, for the same reason why I don't need to read Windows manual. The only exception is when troubleshooting a bug. Then all information sources are more or less important. Actually come to think about it discussion forums are much more important information source, because there you usually can find the same exact problem as you are having. So that auto-generated documentation idea is so last century. :) Those sites should learn from pnp.net, where users can comment on problems they are having straight inside api-reference.
AareP
Oh, you mean the feature they copied from MSDN? Yeah, that could be useful, if only MSDN had it...
configurator
"The integer nearest parameter d. If the fractional component of d is halfway between two integers, one of which is even and the other odd, the even number is returned. Note that this method returns a Decimal instead of an integral type." That doesn't sound very autogenerated.
configurator
@configurator: "Oh, you mean the feature they copied from MSDN?" - strange that this feature is so seldom used then. Maybe because msdn power-users normally use locally installed documentation, without commenting ability. Btw internet commenting doesn't allow deletion of comments: http://msdn.microsoft.com/en-us/library/system.io.file.exists - search for "Testing adding comments" :)
AareP
"That doesn't sound very autogenerated" - sure, those sentences are not auto-generated, but you have probably seen those api-documentations that are generated from source code, and from comments in it. Those kind of documentations are too static..
AareP
One of the reason you rarely find community content in MSDN is that there's already very complete documentation on most subjects. Not all, but most.
configurator