This is the second in a series of three blog posts covering rounding in .NET. In Part 1, we learned that the Math.Round method’s default rounding algorithm probably isn’t what we would expect, which led to many more questions about rounding in general. In Part 2, we’ll try to answer some of those questions by taking a look at some different rounding methods and how they can be implemented in C#.

For the rest of this discussion, let’s assume that “rounding” means “rounding to an integer”. Once you know how to round to an integer, you can round to any precision by multiplying by a factor, rounding to an integer, and then multiplying by the inverse of the factor.

We’ll break the rounding methods into four groups: Round to Nearest (with its many tie breaking rules), Round Ceiling/Floor, Round Toward Zero/Away from Zero, and Round Dither.

### Round to Nearest

The most common rounding algorithm, Round to Nearest simply tries to round to the nearest integer value. If the fraction part of the number is less than 0.5, then the result is the next lower integer value. If the fraction part of the number is greater than 0.5, then the result is the next greater integer value. But what if the fraction part of the number is 0.5, equidistant from the two surrounding integers? In that case, we have to apply some kind of tie breaking rule, and there are at least eight rules from which to choose!

The most commonly used tie breaking rule, and rounding method in general, is **Round Half Away from Zero**, also known as Conventional Rounding or Symmetric Arithmetic Rounding. Rounding a fraction part of 0.5 results in *the nearest integer that is further from zero*. For example, suppose we’re rounding 1.5. The nearest integers are 1.0 and 2.0, so the result would be 2.0 because it’s further from zero. Similarly, -1.5 would round to -2.0.

One way to round a positive number with Round Half Away from Zero is to add 0.5 and then truncate the result. To round a negative number, subtract 0.5 and truncate:

public static int RoundToNearestRoundHalfAwayFromZero(decimal value) { // Add or subtract 0.5 depending on whether the value is positive // or negative, and truncate the result. return (int)(value + 0.5m * Math.Sign(value)); }

The opposite of Round Half Away from Zero is **Round Half Toward Zero**. This tie breaking rule rounds a fraction part of 0.5 to *the nearest integer that is closer to zero*. For example, 1.5 would round to 1.0 and -1.5 would round to -1.0.

To round a positive number using this rule, subtract 0.5 and find the next greater integer. To round a negative number, add 0.5 and find the next lower integer:

public static int RoundToNearestRoundHalfTowardZero(decimal value) { // If value is non-negative, subtract 0.5 and take the next // greater integer. If value is negative, add 0.5 and take the // next lower integer. if (value >= 0) return (int)Math.Ceiling(value - 0.5m); else return (int)Math.Floor(value + 0.5m); }

Instead of rounding toward or away from zero, we could round toward positive or negative infinity instead. The **Round Half Toward Positive Infinity** tie breaking rule specifies that we round a fraction part of 0.5 to *the next greater integer*. For example, 1.5 would round to 2.0 and -1.5 would round to -1.0.

To round to nearest while always rounding a fraction part of 0.5 to the next greater integer, we simply add 0.5 to the number and then find the next lower integer:

public static int RoundToNearestRoundHalfTowardPositiveInfinity(decimal value) { // Add 0.5 and take the next lower integer. return (int)Math.Floor(value + 0.5m); }

The opposite of Round Half Toward Positive Infinity would be the **Round Half Toward Negative Infinity** rule, which instead rounds a fraction part of 0.5 to *the next lower integer*. For example, 1.5 rounds to 1.0 and -1.5 rounds to -2.0.

To round to nearest while taking a fraction part of 0.5 to the next lower integer, we subtract 0.5 from the number and find the next greater integer:

public static int RoundToNearestRoundHalfTowardNegativeInfinity(decimal value) { // Subtract 0.5 and take the next greater integer. return (int)Math.Ceiling(value - 0.5m); }

Another very common tie breaking rule (and, as we discovered in Part 1, the default for .NET’s Math.Round method) is **Round Half to Even** or Banker’s Rounding. As the name suggests, we round a fraction part of 0.5 to *the nearest even integer*. For example, 1.5 and 2.5 both round to 2.0 and -1.5 and -2.5 both round to -2.0.

One way to implement Round Half to Even is by first applying Round Half Toward Positive Infinity. If the result is even, we’re done. Otherwise, we subtract one. If we apply this algorithm to the value 2.5, we first round to 3.0 and then, because 3.0 is not even, we subtract one giving 2.0:

public static int RoundToNearestRoundHalfToEven(decimal value) { // First round half toward positive infinity. Then if the fraction // part of the original value is 0.5 and the result of rounding is // odd, then subtract one. var temp = (int)Math.Floor(value + 0.5m); if (value - Math.Floor(value) == 0.5m && temp % 2 != 0) temp -= 1; return temp; }

Why round to the nearest even integer, though? The **Round Half to Odd** rule rounds a fraction part of 0.5 to *the nearest odd integer* instead. For example, 1.5 rounds to 1.0, 2.5 rounds to 3.0, -1.5 rounds to -1.0, and -2.5 rounds to -3.0. The C# implementation is nearly identical to Round Half to Even:

public static int RoundToNearestRoundHalfToOdd(decimal value) { // First round half toward positive infinity. Then if the fraction // part of the original value is 0.5 and the result of rounding is // even, then subtract one. var temp = (int)Math.Floor(value + 0.5m); if (value - Math.Floor(value) == 0.5m && temp % 2 == 0) temp -= 1; return temp; }

All of the above tie breaking rules have some inherent bias. In order to eliminate the bias, the **Round Half Randomly** rule, also known as Stochastic Rounding, rounds a fraction part of 0.5 *to either the next greater or the next lower integer randomly, with equal probability*. This method effectively eliminates the bias, but the results are not reproducible (unless a pseudo-random number generator with the same seed value is used as the source of randomness).

To implement Round Half Randomly, we use a random number source to generate a value of either zero or one with equal probability. If the value is zero, we apply the Round Half Toward Positive Infinity rule. If the value is one, we apply the Round Half Toward Negative Infinity Rule:

private static readonly Random _Random = new Random(); public static int RoundToNearestRoundHalfRandomly(decimal value) { // Randomly use round to nearest with either the round half toward // positive infinity or round half toward negative infinity rule. if (_Random.Next(0, 2) == 0) return (int)Math.Floor(value + 0.5m); else return (int)Math.Ceiling(value - 0.5m); }

Note that *the above code is not thread-safe*. In particular, there is a race condition in the Random.Next method that can cause the random sequence to become constantly zero after some point. There are several ways to address this, but perhaps the easiest is to avoid having a System.Random instance that persists between method calls. We could change the method so that it rounds each value in a sequence instead of a single value:

public static IEnumerable<int> RoundToNearestRoundHalfRandomly(this IEnumerable<decimal> values) { var random = new Random(); foreach (var value in values) { // Randomly use round to nearest with either the round half toward // positive infinity or round half toward negative infinity rule. if (random.Next(0, 2) == 0) yield return (int) Math.Floor(value + 0.5m); else yield return (int) Math.Ceiling(value - 0.5m); } }

Like Round Half Randomly, the **Round Half Alternatingly** rule also eliminates bias, but the results are more predictable. We alternate between rounding a fraction part of 0.5 to the next lower and the next greater integer. So, for example, if we had the sequence {1.5, 1.5, 1.5, 1.5}, the result would be {1.0, 2.0, 1.0, 2.0}.

To implement Round Half Alternatingly, we start with a source of alternating sequence of zeros and ones. I’ve chosen to implement this using an enumerator that provides an infinite alternating sequence:

private static IEnumerator<int> GetAlternatingEnumerator() { for (; ; ) { yield return 0; yield return 1; } }

Then we can implement the actual rounding method:

private static readonly IEnumerator<int> _AlternatingSequence = GetAlternatingEnumerator(); public static int RoundToNearestRoundHalfAlternatingly(decimal value) { // Use round to nearest with either the round half toward // positive infinity or the round half toward negative infinity // rule based on the current value of the alternating sequence. _AlternatingSequence.MoveNext(); if (_AlternatingSequence.Current == 0) return (int) Math.Floor(value + 0.5m); else return (int)Math.Ceiling(value - 0.5m); }

Again, there are some thread safety issues because we have to maintain state of the alternating sequence between successive calls to our rounding method. Each thread making calls to this method may not get truly alternating rounding if calls from multiple threads are interleaved. The results would be unpredictable, and could be biased. To get around this, we can again use a method that rounds each value in a sequence rather than just a single value:

public static IEnumerable<int> RoundToNearestRoundHalfAlternatingly(this IEnumerable<decimal> values) { var alternatingEnumerator = GetAlternatingEnumerator(); foreach (var value in values) { // Use round to nearest with either the round half toward positive // infinity or the round half toward negative infinity rule based // on the current value of the alternating sequence. alternatingEnumerator.MoveNext(); if (alternatingEnumerator.Current == 0) yield return (int)Math.Floor(value + 0.5m); else yield return (int)Math.Ceiling(value - 0.5m); } }

### Round Ceiling and Round Floor

Sometimes we want to round numbers so that the results are always greater or always lower. At those times, we want to use the Round Ceiling and Round Floor methods. Round Ceiling always rounds a number with a fraction part to *the next greater integer*. Round Floor always rounds to *the next lower integer*. These methods are easily implemented using the Math.Ceiling and Math.Floor methods in .NET:

public static int RoundCeiling (decimal value) { return (int)Math.Ceiling(value); } public static int RoundFloor (decimal value) { return (int)Math.Floor(value); }

### Round Toward Zero and Round Away from Zero

We can pull our values toward zero or push them away from zero, respectively, using the Round Toward Zero and Round Away from Zero rounding methods. As their names suggest, Round Toward Zero rounds every number with a fraction part to *the surrounding integer that’s closer to zero*. Round Away from Zero does just the opposite. Round Toward Zero is equivalent to simple truncation, and we can implement the Round Away from Zero method by using either the Round Ceiling or the Round Floor method depending on whether the number is positive or negative.

public static int RoundTowardZero(decimal value) { // To round toward zero, we can simply truncate by casting to Int32. return (int) value; } public static int RoundAwayFromZero(decimal value) { return value >= 0 ? (int)Math.Ceiling(value) : (int)Math.Floor(value); }

### Round Dither

Some instances, such as rounding values in a slowly changing sequence, may call for an approach that maintains the essence of the original sequence and does not result in long sub-sequences that are constant. The Round Dither method achieves this by rounding numbers with fraction parts using either the Round Ceiling or Round Floor method randomly with a probability that is equal to one minus the fraction part. For example, if we round 1.28 to the nearest integer, then it would be rounded to 1.0 with probability 0.72 or rounded to 2.0 with probability 0.28.

To implement Round Dither in C#, we start with a random number source that can generate fractions between 0 and 1. To round a number, we generate a random fraction, and compare it with the fraction part of the number to be rounded. If the random fraction is less than the fraction part of the number, then we use Round Ceiling. Otherwise, we use Round Floor.

private static readonly Random _Random = new Random(); public static int RoundDither(decimal value) { // If the next random fraction is less than the fraction part of the // number to be rounded, then use Round Ceiling. Otherwise, use // Round Floor. if ((decimal)_Random.NextDouble() < Math.Abs(value - (int)value)) return (int)Math.Ceiling(value); else return (int)Math.Floor(value); }

As was the case with the implementation of the Round Half Randomly tie-breaking rule above, *this code is not thread-safe*. Again, one way that we can address this is by creating a method that rounds each value in a sequence rather than just a single value:

public static IEnumerable<int> RoundDither(this IEnumerable<decimal> values) { var random = new Random(); foreach (var value in values) { // If the next random fraction is less than the fraction part of the // number to be rounded, then use Round Ceiling. Otherwise, use // Round Floor. if ((decimal)random.NextDouble() < Math.Abs(value - (int)value)) yield return (int)Math.Ceiling(value); else yield return (int)Math.Floor(value); } }

### Combined Rounding Results

Before we wrap up, let’s take a look at how the example numbers from Part 1 would be rounded using some of the methods we’ve seen:

-3.5 | -2.8 | -2.5 | -2.2 | 2.2 | 2.5 | 2.8 | 3.5 | |
---|---|---|---|---|---|---|---|---|

Round to Nearest, Round Half Away from Zero | -4.0 | -3.0 | -3.0 | -2.0 | 2.0 | 3.0 | 3.0 | 4.0 |

Round to Nearest, Round Half Toward Zero | -3.0 | -3.0 | -2.0 | -2.0 | 2.0 | 2.0 | 3.0 | 3.0 |

Round to Nearest, Round Half Toward Positive Infinity | -3.0 | -3.0 | -2.0 | -2.0 | 2.0 | 3.0 | 3.0 | 4.0 |

Round to Nearest, Round Half Toward Negative Infinity | -4.0 | -3.0 | -3.0 | -2.0 | 2.0 | 2.0 | 3.0 | 3.0 |

Round to Nearest, Round Half to Even | -4.0 | -3.0 | -2.0 | -2.0 | 2.0 | 2.0 | 3.0 | 4.0 |

Round to Nearest, Round Half to Odd | -3.0 | -3.0 | -3.0 | -2.0 | 2.0 | 3.0 | 3.0 | 3.0 |

Round Ceiling | -3.0 | -2.0 | -2.0 | -2.0 | 3.0 | 3.0 | 3.0 | 4.0 |

Round Floor | -4.0 | -3.0 | -3.0 | -3.0 | 2.0 | 2.0 | 2.0 | 3.0 |

Round Toward Zero | -3.0 | -2.0 | -2.0 | -2.0 | 2.0 | 2.0 | 2.0 | 3.0 |

Round Away from Zero | -4.0 | -3.0 | -3.0 | -3.0 | 3.0 | 3.0 | 3.0 | 4.0 |

All of the above code is available in a single Rounding static utility class here. Note that these aren’t the only possible implementations, or even the most efficient. If you come up with alternate implementations, please post them in the comments.

In Part 2, we have covered most (probably not all) of the algorithms for rounding. Why do we need all of them, though? Can’t we just pick one? In Part 3, we’ll learn why it’s important to use an appropriate algorithm and how you can choose.

**Part 1: The Mystery of Math.Round**

The Math.Round method isn’t as straightforward as one might expect.**Part 2: Exotic Rounding Algorithms**

A menagerie of lesser-known rounding methods, with implementations in C#.**Part 3: Rounding in Action**

Why some rounding algorithms are better than others in certain situations.

Joycelyn says

I see a lot of interesting posts on your page. You have to spend

a lot of time writing, i know how to save you a lot of time, there is

a tool that creates unique, SEO friendly articles in couple of minutes, just search in google – laranita’s free

content source

Jackson says

I read a lot of interesting articles here. Probably you

spend a lot of time writing, i know how to save you a lot of time, there

is an online tool that creates unique, google friendly articles in minutes, just search in google – laranitas free content source

Cleveland says

There are lots of natural remedies that have been found to be very effective and some of

these are even prescribed by doctors as a way of treating

this condition. Your doctor will decide which of these medicines might work the best for you.

The statistics are much higher than we would expect them to be, but this should be a relief,

rather than something to worry about.

Rickey says

It’s enormous that you are getting thoughts from this piece of

writing as well as from our dialogue made here.