# Midpoint Rounding Options in C#

# Midpoint Rounding

Decimal midpoint rounding options in C# default to **To Even**. This was a head scratching moment for me at first, as the way us humans have been taught to round is generally **Away From Zero**.

**Away From Zero** rounds 2.5 to 3; the way most of us were taught rounding at school.

```
var needsRounding = 2.5M;
var rounded = Math.Round(needsRounding, MidpointRounding.AwayFromZero);
Console.WriteLine(rounded);
// prints "3"
```

**To Even** rounds 2.5 to 2. This is often known as **Banker’s Rounding**..

```
var needsRounding = 2.5M;
var rounded = Math.Round(needsRounding, MidpointRounding.ToEven);
Console.WriteLine(rounded);
// prints "2"
```

# Banker’s Rounding

According to MSDN:

(Banker’s Rounding) conforms to IEEE Standard 754, section 4. When used in multiple rounding operations, it reduces the rounding error that is caused by consistently rounding midpoint values in a single direction. In some cases, this rounding error can be significant.

Rounding multiple numbers using **Away From Zero** compounds errors, whereas **To Even** *(Banker’s Rounding)* will round up half the time and round down the other half, so the rounding errors cancel each other out.

Check this out for yourself by summing a load of random numbers:

```
// instantiate some variables to store our sums
decimal actualTotal = 0;
decimal awayFromZeroTotal = 0;
decimal toEvenTotal = 0;
// generate 1,000,000 random numbers; round and sum them
var random = new Random();
for (int i = 0; i < 1000000; i++)
{
// generate a random int from 0 to 1000 and divide it by 1000M (decimal)
// this gives us random decimals to 3 decimal places between 0 and 1
var next = random.Next(1000) / 1000M;
// keep track of our actual total
actualTotal += next;
// round the 3 decimal place numbers to 2 places using the different rounding options
// add the rounded numbers to their respective totals
awayFromZeroTotal += Math.Round(next, 2, MidpointRounding.AwayFromZero);
toEvenTotal += Math.Round(next, 2, MidpointRounding.ToEven);
}
// print out our summed results
Console.WriteLine($"Actual total: {actualTotal}");
Console.WriteLine($"Away from zero: {awayFromZeroTotal}");
Console.WriteLine($"To even: {toEvenTotal}");
// Actual total: 499507.199
// Away from zero: 500008.41
// To even: 499510.54
```

The results are pretty much as you’d expect. We’re summing 1,000,000 numbers between 0 and 1, so you’d expect the result to be around 500,000… which it is.

The actual total (for this particular run-through) was `499507.199`

.
**To Even** rounding result gave us `499510.54`

; pretty close.
**Away From Zero** rounding gave us `500008.41`

, about 500 above actual.

500 is fairly consitent between runs and is due to the size of the population and the rounding precision. Changing the population or precision would yield a different, but none-the-less consitent compounded error.

# Conclusion

So, yeah… banker’s rounding makes sense in this case, and likely in a lot of other cases where the rounded number is used in a later calculation. I can see why it’s the default… just a shame we weren’t taught this way at school!