Python’s round()
Function Doesn’t do What You Think
How to avoid this simple mistake with round() in Python 3
I recently ran into a bug with my Python code that was the result of the last thing I would suspect: rounding. Here’s how to avoid making the same mistake.
The Problem
Basically, I expected the round()
function to operate as rounding usually does, in that half numbers are rounded up to the next whole number. For example, round(4.5)
should return 5
and round(5.5)
should return 6
.
Right?
Surprisingly, that is not how rounding works in Python. Rounding half numbers does not round up, and in fact, it doesn’t always round down either. Instead, it rounds to the nearest even number.
This behavior is shown by the following:
Python 3.7.3
>>> round(1.5)
2
>>> round(2.5)
2
>>> round(3.5)
4
>>> round(4.5)
4
>>> round(5.5)
6
>>> round(6.5)
6
It is worth pointing out that besides the half-number case, round()
works as expected, in that it returns the nearest integer. For example, round(4.4)
returns 4
and round(4.6)
returns 5
.
Note: This is only the case with Python 3. round()
in Python 2 behaves as you would usually expect.
The Solution
We can fix this by defining a new rounding function:
def normal_round(number):
"""Round a float to the nearest integer."""
return int(number + 0.5)
Extended Solution
We can also take this a little further. The round() function also has another argument called ndigits
that specifies how many digits to round to. For example, we can call round(3.14159, 2)
and get 3.14
.
However, we run into a similar issue with rounding half values. Consider the following for example:
Python 3.7.3
>>> round(1.55, 1)
1.6
>>> round(1.65, 1)
1.6
To fix this, we can use a similar solution, and scale it to the specified number of decimal places with the following:
def normal_round(num, ndigits=0):
"""
Rounds a float to the specified number of decimal places.
num: the value to round
ndigits: the number of digits to round to
"""
if ndigits == 0:
return int(num + 0.5)
else:
digit_value = 10 ** ndigits
return int(num * digit_value + 0.5) / digit_value
The if-statement is there so that we get an int
rather than a float
when rounding to 0 digits. This means that rather than normal_round(4.2)
returning 4.0
it will return 4
.
I hope this helps!