Easy Tutorial
❮ Python Pip Install Usage Heap Sort ❯

The Pitfalls of the round Function in Python

Category Programming Techniques

I've always wanted to write about this, but because it's a minor point, I've been reluctant to start. However, I'll still add it, as it's a potential troublemaker if left unaddressed.

The round function is quite simple; it approximates floating-point numbers, retaining a certain number of decimal places. For example:

>>> round(10.0/3, 2)
3.33
>>> round(20/7)
3

The first parameter is a floating-point number, and the second parameter is the number of decimal places to retain, which is optional. If not specified, it defaults to retaining up to an integer.

What pitfalls could such a simple function have?

1. The result of round is related to the Python version

Let's see what differences there are between Python 2 and Python 3:

$ python
Python 2.7.8 (default, Jun 18 2015, 18:54:19) 
[GCC 4.9.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> round(0.5)
1.0
$ python3
Python 3.4.3 (default, Oct 14 2015, 20:28:29) 
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> round(0.5)
0

Is it fun?

If we read the Python documentation, it says the following:

In the Python 2.7 documentation, it ends with "Values are rounded to the closest multiple of 10 to the power minus ndigits; if two multiples are equally close, rounding is done away from 0." The retained value will be closer to the previous side (round half up), and if it's equally far from both ends, it will be rounded away from 0. So round(0.5) will be approximated to 1, and round(-0.5) will be approximated to -1.

But in the Python 3.5 documentation, the document has changed to "values are rounded to the closest multiple of 10 to the power minus ndigits; if two multiples are equally close, rounding is done toward the even choice." If it's equally far from both sides, it will be rounded to the even number. For example, round(0.5) and round(-0.5) will both be rounded to 0, and round(1.5) will be rounded to 2.

So if there is a project that is migrating from Py2 to Py3, pay attention to the use of round (of course, also pay attention to / and //, as well as print, and some more peculiar libraries).

>>> round(2.675, 2)
2.67

Both Python 2 and Python 3 documentation provide the same example, which says:

Note

The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float. See Floating Point Arithmetic: Issues and Limitations for more information.

In simple terms, the result of round(2.675, 2) should be 2.68 whether we look at Python 2 or 3, but it is 2.67. Why? This is related to the precision of floating-point numbers. We know that floating-point numbers may not be precisely represented in the machine because after being converted into a series of 1s and 0s, it may be infinitely long, and the machine has already made a truncation. So the number 2.675 stored in the machine is slightly smaller than the actual number. This slight difference makes it closer to 2.67, so when retaining two decimal places, it is approximated to 2.67.

That's all. Unless there are no requirements for accuracy, try to avoid using the round() function. We have other options for approximate calculations:

❮ Python Pip Install Usage Heap Sort ❯