【Python】浮点数计算时的不准确性以及如何进行精确计算
浮點數一個普遍的問題就是在計算機的世界中,浮點數并不能準確地表示十進制。并且,即便是最簡單的數學運算,也會帶來不可控制的后果。因為,在計算機的世界中只認識0與1
因為在計算機里面,小數是不精確的,例如1.115在計算機中實際上是1.1149999999999999911182,所以當你對這個小數精確到小數點后兩位的時候,實際上小數點后第三位是4,所以四舍五入,因此結果為1.11。
這種說法,對了一半。因為并不是所有的小數在計算機中都是不精確的。例如0.125這個小數在計算機中就是精確的,它就是0.125,沒有省略后面的值,沒有近似,它確確實實就是0.125。
但是如果我們在Python中把0.125精確到小數點后兩位,那么它的就會變成0.12,為什么在這里四舍了?
還有更奇怪的,另一個在計算機里面能夠精確表示的小數0.375,我們來看看精確到小數點后兩位是多少:,為什么這里又五入了?因為在Python 3里面,round對小數的精確度采用了四舍六入五成雙的方式。
如果你寫過大學物理的實驗報告,那么你應該會記得老師講過,直接使用四舍五入,最后的結果可能會偏高。所以需要使用奇進偶舍的處理方法。
例如對于一個小數a.bcd,需要精確到小數點后兩位,那么就要看小數點后第三位: ??如果d小于5,直接舍去;如果d大于5,直接進位;
如果d等于5:d后面沒有數據,且c為偶數,那么不進位,保留c。d后面沒有數據,且c為奇數,那么進位,c變成(c + 1)
python中的decimal模塊可以解決上面的煩惱?
decimal模塊中,可以通過整數,字符串或原則構建decimal.Decimal對象。如果是浮點數,特別注意因為浮點數本身存在誤差,需要先將浮點數轉化為字符串。
當然精度提升的同時,肯定帶來的是性能的損失。在對數據要求特別精確的場合(例如財務結算),這些性能的損失是值得的。
但是如果是大規(guī)模的科學計算,就需要考慮運行效率了。畢竟原生的float比Decimal對象肯定是要快很多的。
Python提供了decimal模塊用于十進制數學計算,它具有以下特點:
1.提供十進制數據類型,并且存儲為十進制數序列;
2.有界精度:用于存儲數字的位數是固定的,可以通過decimal.getcontext().prec=x 來設定,不同的數字可以有不同的精度
3.浮點:十進制小數點的位置不固定(但位數是固定的)
python decimal.quantize()參數rounding的decimal文檔里面的解釋:
ROUND_CEILING (towards Infinity),
ROUND_DOWN (towards zero),
ROUND_FLOOR (towards -Infinity),
ROUND_HALF_DOWN (to nearest with ties going towards zero),
ROUND_HALF_EVEN (to nearest with ties going to nearest even integer),
ROUND_HALF_UP (to nearest with ties going away from zero), or
ROUND_UP (away from zero).
ROUND_05UP (away from zero if last digit after rounding towards zero would have been 0 or 5; otherwise towards zero)
下面通過具體代碼理解每個參數
# -*- coding: utf-8 -*- ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' #作者:cacho_37967865 #博客:https://blog.csdn.net/sinat_37967865 #文件:dealFloat.py #日期:2019-08-01 #備注:python使用浮點類型float計算后,數值可能不對,這時候需要使用decimal模塊 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''from decimal import Decimal,getcontext from decimal import ROUND_UP,ROUND_DOWN,ROUND_HALF_UP,ROUND_HALF_DOWN,ROUND_HALF_EVEN,ROUND_05UP,ROUND_CEILING ,ROUND_FLOORx = 4.20 y = 2.10 a = 1.20 b = 2.30 d = 195.00def float_info():# a.bcd ,精確2位小數點i = 1.115 # 在計算機中實際上是1.1149999999999999911182 四舍五入:1.11j = 0.125 # d后面沒有數據,且c為偶數,那么不進位,保留c 四舍五入:0.12l = 0.1251 # d后面有數據,c變成(c + 1) 四舍五入:0.13k = 0.375 # d后面沒有數據,且c為奇數,那么進位,c變成(c + 1) 四舍五入:0.38print('四舍五入:',round(i,2))print('四舍五入:',round(j, 2))print('四舍五入:',round(l, 2))print('四舍五入:',round(k, 2))def decimal_info():# a.bcd ,精確2位小數點 默認進位方式i = 1.115 # 在計算機中實際上是1.1149999999999999911182 四舍五入:1.11j = 0.125 # d后面沒有數據,且c為偶數,那么不進位,保留c 四舍五入:0.12l = 0.1251 # d后面有數據,c變成(c + 1) 四舍五入:0.13k = 0.375 # d后面沒有數據,且c為奇數,那么進位,c變成(c + 1) 四舍五入:0.38print('浮點型數據不精確:',Decimal(i))print('浮點型數據也可能精確:', Decimal(j))print('傳入的是字符串默認方式四舍五入:', Decimal(str(i)).quantize(Decimal('0.00'))) # 1.12print('傳入的是浮點型默認方式四舍五入:',Decimal(i).quantize(Decimal('0.00'))) # 1.11print('傳入的是浮點型默認方式四舍五入:',Decimal(j).quantize(Decimal('0.00')))print('傳入的是浮點型默認方式四舍五入:',Decimal(l).quantize(Decimal('0.00')))print('傳入的是浮點型默認方式四舍五入:',Decimal(k).quantize(Decimal('0.00')))# 最好傳入字符串print('傳入的是字符串真實四舍五入:', Decimal(str(i)).quantize(Decimal('0.00'), ROUND_HALF_UP))print('傳入的是字符串真實四舍五入:', Decimal(str(j)).quantize(Decimal('0.00'), ROUND_HALF_UP))print('傳入的是字符串真實四舍五入:', Decimal(str(l)).quantize(Decimal('0.00'), ROUND_HALF_UP))print('傳入的是字符串真實四舍五入:', Decimal(str(k)).quantize(Decimal('0.00'),ROUND_HALF_UP))print('傳入的是字符串向上取值:', Decimal('0.121').quantize(Decimal('0.00'), ROUND_UP)) # 向上入 0.13print('傳入的是字符串向下取值:', Decimal('0.129').quantize(Decimal('0.00'), ROUND_DOWN)) # 向下舍 0.12print('傳入的是字符串向上四舍五入:', Decimal('0.125').quantize(Decimal('0.00'), ROUND_HALF_UP)) # 最后一位5向上入 0.13print('傳入的是字符串向下四舍五入:', Decimal('0.125').quantize(Decimal('0.00'), ROUND_HALF_DOWN)) # 最后一位5向下舍 0.12print('傳入的是字符串默認方式四舍五入:', Decimal('0.125').quantize(Decimal('0.00'), ROUND_HALF_EVEN)) # 默認四舍五入方式 0.12 quansize的默認設置值print('傳入的是字符串默認方式四舍五入:', Decimal('0.375').quantize(Decimal('0.00'), ROUND_HALF_EVEN)) # 默認四舍五入方式 0.38print('傳入的是字符串精確位數字為5入:', Decimal('5.351').quantize(Decimal('0.00'), ROUND_05UP)) # 精確的最后一位為0或者5向上入,否者向下舍5.36print('傳入的是字符串精確位數字為0入:', Decimal('5.301').quantize(Decimal('0.00'), ROUND_05UP)) # 精確的最后一位為0或者5向上入,否者向下舍5.31print('傳入的是字符串精確位數字非0和5舍:', Decimal('5.399').quantize(Decimal('0.00'), ROUND_05UP)) # 精確的最后一位為0或者5向上入,否者向下舍5.39print('傳入的是字符串正數時與ROUND_UP一致:', Decimal('5.391').quantize(Decimal('0.00'), ROUND_CEILING)) # 5.40print('傳入的是字符串正數時與ROUND_DOWN一致:', Decimal('5.399').quantize(Decimal('0.00'), ROUND_FLOOR)) # 5.39print('傳入的是字符串負數時與ROUND_DOWN一致:', Decimal('-5.399').quantize(Decimal('0.00'), ROUND_CEILING)) # -5.39 趨近于正無窮print('傳入的是字符串負數時與ROUND_UP一致:', Decimal('-5.399').quantize(Decimal('0.00'), ROUND_FLOOR)) # -5.40 趨近于負無窮print('傳入的是字符串與正負無關,最后一位始終進:', Decimal('-5.399').quantize(Decimal('0.00'), ROUND_UP))def float_deal():z = x + y # 6.300000000000001print(z)if z == 6.30:print("z計算正確")else:print("z計算錯誤")c = a + bprint(c)if c == 3.5:print("c計算正確")else:print("c計算錯誤")e = round(195.00 / 24,2)print(e)if e == 8.13:print("e四舍五入正確")else:print("e四舍五入錯誤")def decimal_deal():getcontext().prec = 3 # 設置精確值,只針對計算時無法除凈時z = Decimal(str(x)) + Decimal(str(y))print(type(z),z)e = Decimal('195.00') / Decimal('24')print(e)if __name__ == '__main__':float_info()decimal_info()#float_deal()#decimal_deal()?
總結
以上是生活随笔為你收集整理的【Python】浮点数计算时的不准确性以及如何进行精确计算的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 跟老齐学python Django实战第
- 下一篇: 线程锁定CPU linux,linux