Today, another Python beginner was misled by the garbage on a Chinese tech blog.

Here’s the beginner’s question:

How do you exactly round floating-point numbers to two decimal places in Python?

If you search Google or Baidu, you will find a lot of articles from CSDN, hundreds, headlines or simple books that say this, but they all say the following:

Rubbish article with no examples

As shown in the following picture, too lazy to tease.

Use round

Examples they give are:

>>> round(1.234.2)
1.23
Copy the code

In this kind of article, he only demonstrates the round, but not the round. So if you change the code a little bit, you’ll find something wrong:

>>> round(11.245.2)
11.24
Copy the code

Zoom in and out

This kind of article is slightly better, know more examples:

However, this article is also full of bugs, as long as you try a few more numbers, in Python 2 and Python 3, the effect is not the same. Let’s look at this in Python 2:

In Python 2, using round directly, 1.125 is accurate to 1.13 to two decimal places, and 1.115 to 1.11 to two decimal places.

Here’s what Python 3 looks like:

Under Python 3, 1.125 is 1.12 when accurate to two decimal places.

His example of zooming in and out in Python 3 is not always correct.

Pack to force goods

There is also a dummy, which is similar to zooming in and out, but he also knows about the decimal module.

But the way he uses it, look at him

The specific reason is unknown ????

Not recommended??

This kind of person should first install a force, said he knew there is such a library, but used to find a problem, and do not know the reason, so it is not recommended to use.

Decimal is a module for high precision computation, and he said that it is not recommended to use decimal.

What’s wrong with Round?

In Python 3, what’s wrong with the built-in function round?

Someone on the Internet says that because decimals are inexact in computers, for example 1.115 is actually 1.114999999999911182 in computers, so when you’re accurate to two decimal places, the third decimal place is actually 4, so you round it off, so you get 1.11.

That is half right.

Because not all decimals are imprecise in a computer. For example, the decimal 0.125 in the computer is exact, it’s just 0.125, there’s no omission, there’s no approximation, it’s exactly 0.125.

But if we were to exact 0.125 to two decimal places in Python, its value would be 0.12:

>>> round(0.125.2)
0.12
Copy the code

Why are you rounding up here?

Even weirder, here’s another decimal that can be accurately represented in a computer, 0.375. Let’s see what it is to two decimal places:

>>> round(0.375.2)
0.38
Copy the code

Why is there another five in here?

In Python 3, the accuracy of round is rounded to six and doubled to five.

If you’ve ever written a college physics lab report, you’ll probably remember that your teacher told you that using rounded numbers can lead to higher results. So you need to use an odd and even approach.

For example, if a decimal a.bcd is accurate to two decimal places, look at the third decimal place:

  1. ifdIf it’s less than 5, I just drop it
  2. ifdIf it’s greater than 5, we carry it
  3. ifdIs equal to 5:
    1. dThere is no data behind, and c isAn even numberSo we don’t carry, we keep c
    2. dThere is no data behind, and c isAn odd numberSo, carry, c becomes c plus 1.
    3. ifdAnd then there are non-zero numbers, for example the decimal is actually zeroa.bcdefSo, you have to carry, so c becomes c plus 1.

If you are interested in odd even rounding, you can search for two terms on Wikipedia: numerical modification and odd even rounding.

So if round’s results are not what you expect, you need to consider two reasons:

  1. Can your decimal be accurately stored on the computer? If not, then it probably doesn’t meet the criteria for rounding, for example1.115To the third decimal place, it’s actually4Of course it will be abandoned.
  2. If your decimal could be represented accurately in a computer, then,roundThe carry mechanism used isOdd into evenSo it depends on which digit you want to keep, whether it’s odd or even, and whether there’s data after it.

How to round properly

If we want to do our mathematical rounding, we need to use the Decimal module.

How to use the Decimal module properly?

Read the official document, do not read Chinese spam blog!!

Read the official document, do not read Chinese spam blog!!

Read the official document, do not read Chinese spam blog!!

Don’t worry about reading English, Python has an official Chinese document (some of the functions are not translated yet).

Let’s take a look: docs.python.org/zh-cn/3/lib…

The official document gives the details of how to write:

>>>Decimal('1.41421356').quantize(Decimal('1.000'))
Decimal('1.414')
Copy the code

So let’s test 0.125 and 0.375 with two decimal places:

>>> from decimal import Decimal
>>> Decimal('0.125').quantize(Decimal('0.00'))
Decimal('0.12')
>>> Decimal('0.375').quantize(Decimal('0.00'))
Decimal('0.38')
Copy the code

Why is it the same as round? Let’s take a look at the quantize function prototype and documentation in the documentation:

It is mentioned here that carrying can be determined by specifying the Robot parameter. If the Robot argument is not specified, the robot method provided by the context is used by default.

Now let’s look at what carry is in the default context:

>>> from decimal import getcontext
>>> getcontext().rounding
'ROUND_HALF_EVEN'
Copy the code

As shown in the figure below:

ROUND_HALF_EVEN is actually an odd even! If we want to specify true rounding, we need to specify ROUND_HALF_UP in the quantize:

>>> from decimal import Decimal, ROUND_HALF_UP
>>> Decimal('0.375').quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
Decimal('0.38')
>>> Decimal('0.125').quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
Decimal('0.13')
Copy the code

Everything seems normal now.

Will anyone further ask what happens if Decimal takes floating point numbers instead of strings?

Try this:


>>> Decimal(0.375).quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
Decimal('0.38')
>>> Decimal(0.125).quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
Decimal('0.13')
Copy the code

Does that mean that in the first argument to Decimal, you can pass floating point numbers directly?

Let’s try another number:

>>> Decimal(11.245).quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
Decimal('11.24')
>>> Decimal('11.245').quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
Decimal('11.25')
Copy the code

Why is floating point 11.245 different from string 11.245?

We continue to search for answers in the documents.

The official documentation makes it very clear that if you pass in a floating point value that cannot be stored exactly in the computer, it will be converted to an imprecise binary value and then converted to its decimal equivalent.

For an inexact decimal, when you pass it in, the number will have been converted to an imprecise number before Python gets it. So you pass in 11.245, but Python actually gets 11.244999999999… .

However, if you pass in the string ‘11.245’, Python will know when it gets it that it is 11.245 and will not be converted to an imprecise value in advance. Therefore, it is recommended to pass a string floating-point number as the first argument to Decimal instead of writing a floating-point number directly.

In summary, if you want to do a precise rounding, your code should read:

from decimal import Decimal, ROUND_HALF_UP

origin_num = Decimal('11.245')
answer_num = origin_num.quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
print(answer_num)
Copy the code

The running effect is shown in the figure below:

In particular, once you are doing an exact calculation, you should never use floating-point numbers alone, but always use Decimal(‘ floating-point ‘). Otherwise, when you assign values, the precision is lost. It is recommended to use Decimal throughout:

a = Decimal('0.1')
b = Decimal('0.2')
c = a + b
print(c)
Copy the code

Finally, if there are students who want to know why 0.125 and 0.375 can be stored accurately, but 1.115 and 11.245 cannot be stored accurately, please leave a comment at the bottom of this article. If there are more students who want to know, I will write an article to explain.

At the end of the day, if you can’t read English and the Chinese document is too boring, go for gold. Nugget’s high quality Chinese articles are 10,000 times higher than CSDN’s.

If this article is helpful to you, please consider following my wechat official account: Unheard Code (ID: istkingName)