15,615,651 members
Articles / Programming Languages / C#
Tip/Trick
Posted 23 Dec 2018

56.1K views
28 bookmarked

# How .NET's Math.Round has Nothing to do with Maths. And That's OK!

Rate me:
About the unexpected behaviour of .NET's Math.Round

## Introduction

Before we start, imagine what values "a" and "b" would have after running the following:

C#
a = Math.Round(1.5);
b = Math.Round(2.5);

If your answer is "a = 2 and b = 3", you are, just like I was, wrong. The correct answer is that both a and b end up becoming 2. Confused? You might want to stick around for the reason why.

## Background

About a week ago, I was working on some code that was moving two entities along the x-axis. Inside the library, their x value was a double, but the public interface only allowed for meters in the form of an integer. I used Math.Round to translate the double value into that integer.

One of the requirements for the code was that the entities could never be closer than one meter to each other. I used the rounded value to check this.

As you might have guessed by now, the code kept failing. Even though both entities started out exactly one meter apart, had the same starting speed and were accelerating with the same speed, using the same algorithm, the integer value kept returning the same x value once in a while and thus the code kept failing.

At first, I thought it was some kind of precision error as a result of using double values. But this wouldn't make much sense, as the algorithm would result in the same precision loss in both values. After some debugging, I eventually found out it was not the double but Math.Round that was behaving differently than I had anticipated.

## Bankers Rounding

After I had figured out the problem was with the use of Math.Round, I quickly figured out the problem: the default[1] implementation for rounding in .NET is "Round half to even"[2] a.k.a. "Bankers Rounding". This means that mid point values are rounded towards the nearest even number. In the example I provided in the introduction, this means both values are being round towards 2, the nearest even number.

So why was it implemented like this in .NET? Is it a bug?

Well, when first implemented, Microsoft followed the IEEE 754 standard. This is the reason of the default Math.Round implementation.[3] (Note: The current IEEE 754 standard contains five rounding rules[4])

Another good reason is that bankers rounding does not suffer from negative or positive bias as much as the round half away from zero method over most reasonable distributions.

## Round Half Away From Zero

But all is not lost.

If you expect 1.5 to be rounded to 2 and 2.5 to be rounded to 3 (like I expected), the Math.Round method has an override that allows you to use the "Round half away from zero"[5] method instead.

C#
a = Math.Round(1.5, MidpointRounding.AwayFromZero);
b = Math.Round(2.5, MidpointRounding.AwayFromZero);

In the code snippet above, 1.5 is rounded to 2 and 2.5 is rounded to 3.

## Round Half Up

Interestingly however, after doing some research, it looks like "Round half away from zero" is also not the method commonly used in maths. Instead, the "Round half up"[6] method is usually used[7] in maths. For positive numbers, there is no difference, but for negative numbers, mid point values are rounded towards +∞ instead of away from 0.

This way, there are the same amount of fractions being rounded to zero, while in the "Round half away from zero" method, both 0.5 and -0.5 are not rounded to zero, making zero an exception to all other numbers.

## Math ≠ maths

So it turns out that the Math.Round method does not even support the actual rounding method that is commonly used in maths.

When I first discovered this, I was quite disappointed. After all, even with the good reasons that were provided, the library is still called Math. It does seem to communicate a certain intent.

But writing this article did make me think: before looking into it, I was not even aware there were so many rounding methods. Each with its own pros and cons. Each with its own consequences. If I didn't know about all these methods, I certainly did not know about those consequences. So maybe it is not such a bad thing that someone else did this for me, and made the appropriate "default" decision.

After all, if the consequences of your rounding are that critical to your code, I do hope that you don't make the assumptions I did, or at least discover they were wrong with some good old fashion tests.

## Wrapping Up

Even though this subject isn't really brain surgery (or rocket science for that matter), I do hope you learned something new, or at least enjoyed the read.

In the end, I think this is a good reminder of how complex even the seemingly simple things we use every day can be. And there is probably a lesson about assumptions in here as well. ;)

Happy rounding!

## Other Programming Languages

After discussing this subject with others, I got curious how other languages handle the mid point values. I was happily surprised that almost every language did have this subject covered. However, there are a lot of differences between languages.

To hopefully help someone in the future, I have mapped the rounding methods in a table. X's in bold are default implementations, other X's are optional parameters or separate methods.

In an attempt to not clutter the references list, the language names in the table link to the documentation I used.

Without further ado, the big "programming language/rounding method table":

 Round half up Round half down Round half towards zero Round half away from zero Round half to even Round half to odd Random tie-breaking Java C Python* C++ VB .NET Java Script C# PHP Objective C MATLAB R Perl Swift Go Delphi Ruby** X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X

* The table references Python 3.7 but Python 2 actually has a different round implementation. It will round half away form zero.
** The mode keywords in Ruby are :up for "round half away from zero" and :down for "round half towards zero", making them quite confusing in my opinion.

As I have no experience with most of these languages, feel free to point out any mistakes. I will correct the table accordingly.

## History

• 23-12-2018 - Version 1
• 23-12-2018 - Version 1.1
• Minor textual changes
• 24-12-2018 - Version 1.2
• Clarified example
• 26-12-2018 - Version 1.3
• Minor textual change
• 05-01-2018 - Version 2.0

Written By
Software Developer (Senior)
Netherlands
Enthusiastic Senior Software Engineer.

Experience working in a multinational and multidisciplinary environment with involvement in all stages of the software development process. Supportive and reliable team player with a strong aim towards software quality and customer satisfaction. Fast and eager learner.

His passion for programming can be traced back to his pre-professional days. Where, even as an elementary school student he could be found on the computer creating computer games. The reason? There is just no feeling like being able to think something up, create it, and then see others enjoy it.

Outside the office, he's a contributor to the Code Project and there is always a project he's working on. When he's not coding he likes to make and edit video’s, can discuss theoretical physics for hours and if you challenge him to a board game, he won’t say no. He can also frequently be found in the gym and travels when he can.

 FirstPrev Next
 Re: "Maths" LOL Pong God26-Dec-18 4:51 Pong God 26-Dec-18 4:51
 Re: "Maths" LOL Jasper Lammers27-Dec-18 0:15 Jasper Lammers 27-Dec-18 0:15
 Re: "Maths" LOL DaveBoltman1-Jan-19 20:55 DaveBoltman 1-Jan-19 20:55
 Re: "Maths" LOL Pong God7-Jan-19 9:43 Pong God 7-Jan-19 9:43
 Re: "Maths" LOL JRickey7-Jan-19 11:48 JRickey 7-Jan-19 11:48
 Re: "Maths" LOL Pong God7-Jan-19 17:26 Pong God 7-Jan-19 17:26
 Re: "Maths" LOL DaveBoltman7-Jan-19 21:09 DaveBoltman 7-Jan-19 21:09
 Re: "Maths" LOL Frank Malcolm21-May-22 19:37 Frank Malcolm 21-May-22 19:37
 And nobody ever says "mathematic" verbally. I don't cringe at our US friends saying "math", it's just one of their odd expressions... like saying "aluminum" instead of "aluminium" (mostly) or "soder" instead of "solder" (some people).
 Excellent Thanks787225-Dec-18 7:34 Thanks7872 25-Dec-18 7:34
 Similar discovery on rounding nedcove25-Dec-18 7:24 nedcove 25-Dec-18 7:24
 Re: Similar discovery on rounding Jasper Lammers25-Dec-18 20:31 Jasper Lammers 25-Dec-18 20:31
 .Net uses the wrong (unexpected) default parameter snoopy00125-Dec-18 1:57 snoopy001 25-Dec-18 1:57
 Re: .Net uses the wrong (unexpected) default parameter Jasper Lammers12-Jan-19 11:04 Jasper Lammers 12-Jan-19 11:04
 My vote of 5 Robert_Dyball23-Dec-18 23:50 Robert_Dyball 23-Dec-18 23:50
 Re: My vote of 5 Jasper Lammers24-Dec-18 23:18 Jasper Lammers 24-Dec-18 23:18
 rounding error in round Paul Tait23-Dec-18 15:44 Paul Tait 23-Dec-18 15:44
 Re: rounding error in round Jasper Lammers24-Dec-18 23:21 Jasper Lammers 24-Dec-18 23:21
 Last Visit: 31-Dec-99 18:00     Last Update: 30-Mar-23 15:11 Refresh 12