Python 所有文章傳送門 |
---|
【Python】所有文章傳送門 |
目錄
- 簡述 / 前言
- 1. Python 實驗
- 2. 自定義函數
- 3. 總結
簡述 / 前言
最近心血來潮,剛復習到折半插入排序時,發現算法的mid
(中間點)選擇的公式是:(low + high)/2
,那么當(low + high)
出現奇數時怎么辦呢,比如(low + high)/2 = 1.5
,那么是取 1
還是 2
呢?于是我在 Python
中實驗了一下,由于 Python 中浮點數運算結果還是浮點數,因此我用了內置函數 round(number, ndigits)
來實現小數轉整數運算。
1. Python 實驗
>>> round(1.5, 0)
2.0
看樣子好像是四舍五入的取法,但是換了一個數字之后,就發現新大陸了!
>>> round(2.5, 0)
2.0
這個運算發現round其實就是直接截取了整數部分,那么事實果真如此嗎?文末會給出解答~
2. 自定義函數
第一反應就是官方的函數是不是有問題,于是找了幾篇博客(參考的一個博客解答,但并沒有完全解決)看看怎么回事,發現給出的解決方法都不能解決問題,實驗如下:
>>> from decimal import Decimal
>>>
>>> print(round(Decimal("1.5"),0))
2
>>> print(round(Decimal("2.5"),0))
2
>>> print(Decimal("1.5").quantize(Decimal("0.")))
2
>>> print(Decimal("2.5").quantize(Decimal("0.")))
2
于是一股腦的自己花了十幾分鐘寫了一個函數來實現:
def round_Pro(number, ndigits):"""實現四舍五入:param number: 要四舍五入的數字:param ndigits: 要保留的位數:return: 以 float 形式輸出"""flag = '' # 用于記錄 number 的正負性if number < 0:flag = '-'number_abs = abs(number) # 一律采用正數的四舍五入進行操作number_abs_str = str(number_abs)if '.' in number_abs_str: # 如果給的數字是小數的話integer = int(number_abs_str.split('.')[0]) # 數字的整數部分decimal_str = number_abs_str.split('.')[1] # 數字的小數部分decimal_cnt = len(decimal_str) # 小數位數if ndigits >= decimal_cnt: # 如果要保留的小數位數 >= 整個小數位數,則直接輸出原數字return numberelse: # 要保留的小數位數 < 整個小數位數,需要進行四舍五入judge_num = int(decimal_str[ndigits]) # 舍入位數字(根據此數字判斷是否需要進位)if ndigits == 0: # 只保留整數decimal = 0 # 小數設為 0if judge_num >= 5: # 如果小數第一位 > 5,則整數進一位integer += 1else: # 保留指定小數位數decimal = int(decimal_str[:ndigits]) # 要保留的小數(直接截斷轉為整數,便于后續直接計算進位)decimal_cnt = len(decimal_str[:ndigits]) # 要保留的小數位數if judge_num >= 5: # 如果判斷位(指定保留小數位數后一位的小數數字,比如:number=1.234,ndigits=2,那么判斷位就是數字"4") > 5decimal += 1 # 小數進一位if len(str(decimal)) > decimal_cnt: # 如果進位導致小數位最高位也進位了integer += 1 # 整數部分也要進位decimal = str(decimal)[1:] # 小數部分去掉最高位(由于進位多出來的數據)decimal = str(decimal)return float(flag + str(integer) + '.' + decimal)else: # 給的數字是一個整數,直接輸出return number
測試如下:
print(round_Pro(2.9897, 5))
print(round_Pro(2.4497, 1))
print(round_Pro(2.4497, 0))
print(round_Pro(2.5597, 0))
print(round_Pro(2.5597, 10))
print(round_Pro(0, 0))
print(round_Pro(0, 10))
print(round_Pro(-1.5, 0))
print(round_Pro(-1.5, 1))
print(round_Pro(-1.56, 1))
print(round_Pro(-1.56, 2))
print(round_Pro(-1.999, 2))
輸出如下:
2.9897
2.4
2.0
3.0
2.5597
0
0
-2.0
-1.5
-1.6
-1.56
-2.0
寫完之后發現我的邏輯其實有點復雜(先轉為字符串進行分割,再分別轉成數字進行計算),于是又找找看有沒有其它的解決方法,因為我堅信我不可能是第一個遇到這種問題的人,于是很快發現了另一篇博客(能夠解決此問題的一篇博客),完全解決了這個問題,即用到一個庫:decimal
,只保留整數就是 Decimal("1.")
,保留1位小數就是 Decimal(".1")
,2位小數就是 Decimal(".01")
【Decimal(".89")
也是一樣的,本質就是看你給的字符串中小數占了多少位而已】。
>>> from decimal import Decimal
>>> print(Decimal(1.5).quantize(Decimal("1."), rounding="ROUND_HALF_UP"))
2
>>> print(Decimal(2.5).quantize(Decimal("1."), rounding="ROUND_HALF_UP"))
3
3. 總結
解決方法,使用第三方庫函數,格式是:Decimal(要進行四舍五入的數字).quantize(Decimal("保留多少位小數"), rounding="ROUND_HALF_UP")
:
>>> from decimal import Decimal
>>> num = 1.5
>>> print(Decimal(num).quantize(Decimal("1."), rounding="ROUND_HALF_UP"))
2
那么回到文章開頭提出的問題,round為什么不能進行四舍五入呢? 其實round本質就是四舍五入,但是由于我們的計算機存儲數字的精度有問題,才導致了這種情況發生:
>>> from decimal import Decimal
>>> print(Decimal(1.535))
1.5349999999999999200639422269887290894985198974609375
>>> print(Decimal(1.725))
1.725000000000000088817841970012523233890533447265625
詳見 python3 小數位的四舍五入(用兩種方法解決round 遇5不進),文中給出了兩種解決方法。