Why does Math.Round(2.5) return 2 instead of 3?
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 aDouble
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:
Round(Decimal)
/Round(Decimal, MidpointRounding)
Round(Double)
/Round(Double, MidpointRounding)
Round(Decimal, Int32)
/Round(Decimal, Int32, MidpointRounding)
Round(Double, Int32)
/Round(Double, Int32, MidpointRounding)
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.)
That's called rounding to even (or banker's rounding), which is a valid rounding strategy for minimizing accrued errors in sums (MidpointRounding.ToEven)
. The theory is that, if you always round a 0.5 number in the same direction, the errors will accrue faster (round-to-even is supposed to minimize that) (a).
Follow these links for the MSDN descriptions of:
Math.Floor
, which rounds down towards negative infinity.Math.Ceiling
, which rounds up towards positive infinity.Math.Truncate
, which rounds up or down towards zero.Math.Round
, which rounds to the nearest integer or specified number of decimal places. You can specify the behavior if it's exactly equidistant between two possibilities, such as rounding so that the final digit is even ("Round(2.5,MidpointRounding.ToEven)
" becoming 2) or so that it's further away from zero ("Round(2.5,MidpointRounding.AwayFromZero)
" becoming 3).
The following diagram and table may help:
-3 -2 -1 0 1 2 3
+--|------+---------+----|----+--|------+----|----+-------|-+
a b c d e
a=-2.7 b=-0.5 c=0.3 d=1.5 e=2.8
====== ====== ===== ===== =====
Floor -3 -1 0 1 2
Ceiling -2 0 1 2 3
Truncate -2 0 0 1 2
Round(ToEven) -3 0 0 2 3
Round(AwayFromZero) -3 -1 0 2 3
Note that Round
is a lot more powerful than it seems, simply because it can round to a specific number of decimal places. All the others round to zero decimals always. For example:
n = 3.145;
a = System.Math.Round (n, 2, MidpointRounding.ToEven); // 3.14
b = System.Math.Round (n, 2, MidpointRounding.AwayFromZero); // 3.15
With the other functions, you have to use multiply/divide trickery to achieve the same effect:
c = System.Math.Truncate (n * 100) / 100; // 3.14
d = System.Math.Ceiling (n * 100) / 100; // 3.15
(a) Of course, that theory depends on the fact that your data has an fairly even spread of values across the even halves (0.5, 2.5, 4.5, ...) and odd halves (1.5, 3.5, ...).
If all the "half-values" are evens (for example), the errors will accumulate just as fast as if you always rounded up.
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.