Python四捨五入與保留小數位數(精確舍入與保留)

本文深入講解Python3中 四捨五入、 截斷保留 與 保留小數位數、取整,幫助Python初學者排坑!
總結不易,轉載請標明出處:https://blog.csdn.net/weixin_41683971/article/details/105027654

0. 前言(吐槽)

作爲一個Python初學者,偶然遇到需要利用Python3進行四捨五入的操作,就想着查一查有沒有什麼簡單快捷的小函數一步到位。這不查不得了,一查就暈倒,網上的帖子和博客漫天飛,閉着眼睛瞎吹的文章一大堆,爲了弄懂這一塊,費了很多時間和精力。
剛開始很迷,畢竟是個Python小白,咱也不知道人家說的到底對不對,那就多看幾個唄,看看咋回事,可誰成想越看越亂。
一開始基本都是“round函數可進行四捨五入運算”,然而我驗證了一下發現round壓根不行;
又看到有人說用格式控制字符“%.xf”直接就四捨五入到保留位數,emmm就仗着我是小白瞎告訴我唄;
再後來有個稍微好一點的,說用decimal模塊的Decimal()+quantize(),還舉了倆例子驗證了下,emmm還自己把自己給驗證錯了,還說“原因未知,精度不高,慎用”…
於是我就自己去查了Python3.8.2的官方文檔,看了decimal模塊和round函數的說明,才恍然大悟。

好了,不多說,上乾貨!

1. 四捨五入(精確)

1.1 四捨五入並保留x位小數——用decimal模塊中的Decimal方法(精度高)

① 數據存儲需爲字符串型

Decima(a)中的a需要是字符串形式的數字,而不能直接是浮點型數字,因爲浮點型數據本身就不是精確的。Why?

假設你輸入的是0.1+0.1+0.1,你以爲是0.3(是你臆想的十進制運算得到0.3),但在計算機存儲中是以二進制浮點數存儲的(計算機會自動將你的十進制0.3轉化爲二進制0.3,但是計算機的二進制存儲可不像0.3這麼精確),其實它對應的是十進制的0.300000000031或其他,因此並不是精確的。

>>> 0.1 + 0.1 +0.1 == 0.3
False
>>> 0.1 + 0.1 + 0.1
0.30000000000000004

而使用字符串存儲輸入的數據就可以精確存儲輸入的數據,即輸入是多少輸出就是多少。

② 模塊及函數引入

導入decimal模塊,則調用模塊中函數時需用 模塊名.函數名(參數) 的形式’’

>>> import decimal
>>> a = "1.345"
>>> a_t = Decimal(a).quantize(Decimal("0.01"), rounding = "ROUND_HALF_UP")
>>> print(a_t)
1.35

導入decimal模塊中的Decimal函數,則調用時只需用該模塊中的 函數名(參數) 的形式

>>> from decimal import Decimal
>>> a = "1.345"
>>> #保留幾位小數由像第二個括號中的幾位小數決定,即保留兩位小數,精確到0.01
>>> #如果想要四捨五入保留整數,那麼第二個括號中就應該爲"1."
>>> a_t = Decimal(a).quantize(Decimal("0.01"), rounding = "ROUND_HALF_UP")
>>> print(a_t)
1.35

③ rounding的舍入方式

decimal模塊中默認rounding=ROUND_HALF_EVEN(可以通過getcontext().rounding命令查看), 即銀行家式舍入法——四捨六入五成雙式舍入法。

要想使用decimal模塊中的Decimal()+quantize()方法實現精確的四捨五入,需要指定rounding=ROUND_HALF_UP——四捨五入法。

④ rounding舍入方式的指定

需要指出的是,如果僅從decimal模塊中引入Decimal方法,那麼對rounding = “ROUND_HALF_UP”應該在quantize()方法內指出,如下:

>>> from decimal import Decimal# 引用decimal模塊中的Decimal方法
>>>
>>> a = '1.345'
>>> b = '2.345' # 需要用字符串存儲要處理的小數
>>> # quantize內需要指出rounding爲四捨五入方式
>>> a2 = Decimal(a).quantize(Decimal("0.01"), rounding = "ROUND_HALF_UP")
>>> b2 = Decimal(b).quantize(Decimal("0.01"), rounding = "ROUND_HALF_UP")
>>>
>>> print(a2)
1.35
>>> print(b2)
2.35

如果想單獨使用getcontext().rounding = “ROUND_HALF_UP”來指定舍入方式爲四捨五入方式,那就不能只引進Decimal方法,而需要引用整個decimal模塊,如下:

>>> import decimal # 引用decimal模塊,下面調用模塊中的函數時不要忘記加decimal.前綴
>>>
>>> a = "1.345"
>>> b = "2.345" # a, b以字符串存儲想要處理的小數
>>> A = 1.345
>>> B = 2.345   # A, B以浮點型存儲想要處理的小數
>>> # 單獨指出rounding爲四捨五入方式
>>> decimal.getcontext().rounding = "ROUND_HALF_UP"# 不要忘記decimal.的前綴
>>> # quantize內無需再指出rounding
>>> a2 = decimal.Decimal(a).quantize(decimal.Decimal("0.01"))
>>> b2 = decimal.Decimal(b).quantize(decimal.Decimal("0.01"))
>>> A2 = decimal.Decimal(A).quantize(decimal.Decimal("0.01"))
>>> B2 = decimal.Decimal(B).quantize(decimal.Decimal("0.01"))
>>>
>>> print("a2 = ", a2)
a2 =  1.35
>>> print("b2 = ", b2)
b2 =  2.35
>>> print("A2 = ", A2)
A2 =  1.34
>>> print("B2 = ", B2)
B2 =  2.35

1.2 四捨五入後取整

① 使用Decimal方法,指定保留位數的括號內爲”%1.”即可

>>> from decimal import Decimal
>>> a = '1.5'
>>> b = '2.5'
>>> #保留幾位小數由像第二個Decimal括號中的幾位小數決定,即保留兩位小數,精確到0.01
>>> #如果想要四捨五入保留整數,那麼第二個括號中就應該爲"1."
>>> a_int = Decimal(a).quantize(Decimal("1."), rounding = "ROUND_HALF_UP")
>>> b_int = Decimal(b).quantize(Decimal("1."), rounding = "ROUND_HALF_UP")
>>>
>>> print(a_int)
2
>>> print(b_int)
3

② 使用int(a+0.5)方法(int爲向0取整)

>>> a = 1.5
>>> b = 2.5
>>>
>>> a_int_roundup = int(a + 0.5)  # 對浮點數a先+0.5,再截斷取整int,可實現四捨五入
>>>
>>> print("a_int = %d" % a)  # 對浮點數a直接用%d的格式控制符來控制輸出整形數據,輸出的a_int爲截斷的整形數據(並未四捨五入)
a_int = 1
>>> print("a_int_roundup = %d" % a_int_roundup)   # 輸出四捨五入後的整形數
a_int_roundup = 2




凡是使用float型數據進行存儲和處理,都不能達到像decimal模塊這樣的高精度!

以下2—5的精度均不能實現精確的五入(四捨六入可實現)

2. 非精確的舍入

2.1 print("%.xf" % a)形式

>>> a = 1.15
>>> b = 1.25
>>> c = 1.35
>>> A = 1.345
>>> B = 2.345
>>>
>>> print("a_1 = %.1f" % a)
a_1 = 1.1
>>> print("b_1 = %.1f" % b)
b_1 = 1.2
>>> print("c_1 = %.1f" % c)
c_1 = 1.4
>>> print("A_2 = %.2f" % A)
A_2 = 1.34
>>> print("B_2 = %.2f" % B)
B_2 = 2.35

可見,對於利用格式控制字符來實現保留一定的小數位數並不能實現精確的四捨五入,也不是截斷式保留。

Note: 對一個float型數據a,用print(“%d” % a),其結果是輸出截斷浮點數的整形數據。

2.2 format()形式

可針對利用變量x指定保留位數時,用format()形式,如:

>>> import math
>>> n, x = map(int, input().split()) # 一行輸入兩個數據,底數爲n,保留位數爲x
5 3
>>> a = n ** math.pi# 計算n的π次方
>>> print("%.{}f".format(x) % a)
156.993

2.3 round函數(精度低,儘量避免使用)

關於round函數,初期看了很多網上的帖子和博客,我也是醉了,有說是“四捨五入”規則的,有說是“四捨六入五成雙”規則的,然而在我試驗了僅僅幾個數據之後就發現,壓根沒有這麼簡單。

>>> round(2.675,2)
2.67
>>> round(1.5)
2
>>> round(2.5)
2
>>> round(5.215,2)
5.21

就這幾個例子就首先可以斷定,round不是四捨五入的舍入方式了。


再來看一下什麼是“四捨六入無成雙”的原則,下面是維基百科和360百科的介紹:
維基百科——奇進偶舍
360百科——四捨六入五成雙

在這裏插入圖片描述
在這裏插入圖片描述
但是當我輸入以下幾個例子時,發現round函數並不是這麼回事。

>>> round(5.215,2)
5.21
>>> round(5.225,2)
5.22
>>> round(5.675,2)
5.67
>>> round(5.275,2)
5.28

如round(5.215,2)是保留兩位小數,而原數據第3位是5,且後面沒有有效數字,第2位是1,爲奇數,按照“奇進偶舍”的原則,結果應該是5.22,但是實際卻是輸出5.21;
再比如round(5.675,2)是保留兩位小數,而原數據第3位是5,且後面沒有有效數字,第2位是7,爲奇數,按照“奇進偶舍”的原則,結果應該是5.68,但是實際卻是輸出5.67;
再比如round(5.275,2),和round(2.675,2)相比小數點後第2位沒有改變,僅改變了小數點後第1位,第2位仍爲奇數,但是這裏保留兩位小數的結果卻有了進位。

綜上,可以看出,round函數既不是“四捨五入”的原則,也不是“四捨六入無成雙”的原則

那麼到底是怎樣的原則呢?

  • round函數的原則:(大前提——數值靠近哪邊往取保留值)
    以下是Python3.8.2官方標準庫文檔的說明:
    在這裏插入圖片描述

  • round函數的另一侷限性:
    當希望保留指定位數的小數的末尾有多個連續的0時,會從後往前舍掉無用的0。即使指定了保留的位數也無法保留這些0,但是指定保留位數的情況下舍掉無用的0後至少也會保留到小數點後1位,而不會得到一個整數,如:

>>> round(1.2000,3)
1.2
>>> round(1.000,2)
1.0

對於round函數的大坑,可以參考一下這個鏈接

友情提醒: 除非對精確度沒什麼要求,否則儘量避開用round()函數。如果對精度有要求,最好使用decimal模塊。

3. 截斷保留n位小數

① 用浮點數處理——先放大指定的倍數n倍→然後取整int→再除以和放大倍數相等的倍數n倍

>>> print(int(1.23456 * 10000) / 10000)
1.2345

② 以字符串形式直接截斷保留指定小數位數

# 以字符串形式截斷保留指定小數位數
str, n = input(), int(input()) # 輸入的小數以字符串形式存入str中,指定的保留位數以整數存入n中

for i in range(0, len(str)):

      if str[i] == '.':
            print(str[i], end = '')
            for j in range(1, n+1):
                  print(str[i+j], end = '')
            break

      else:
            print(str[i], end = '')

在這裏插入圖片描述

4. 一步取整方法

4.1 向上/下取整(ceil(a)函數與floor(a)函數)

import math

# 向上取整
>>> import math
>>> print("math.ceil(2.3) => ", math.ceil(2.3))
math.ceil(2.3) =>  3
>>> print("math.ceil(2.6) => ", math.ceil(2.6))
math.ceil(2.6) =>  3

# 向下取整
>>> print("math.floor(2.3) => ", math.floor(2.3))
math.floor(2.3) =>  2
>>> print("math.floor(2.6) => ", math.floor(2.6))
math.floor(2.6) =>  2

4.2 向靠近0的一側取整

>>> b = int(2.6)
>>> print(b)
2

或者直接:

>>> print("%d" % 2.65)
2

還有上面介紹的小數的四捨五入取整


以上就是我在學習Python3過程中對“四捨五入、截斷保留與保留小數位數、取整”的總結,希望能夠爲初學者提供一些幫助,避免這個地方的小坑。

PS:對函數使用有問題最好查看官方文檔Python 3.8.2 文檔(這次算是發現了,網上的帖子真是坑太多了…)

參考博客:
博客1
博客2
博客3

如有錯誤,歡迎批評指正。
總結不易,轉載請標明出處:https://blog.csdn.net/weixin_41683971/article/details/105027654

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章