Python3浮點型(float)運算結果不正確處理辦法

1、問題說明

之前對浮點數運行沒有沒有太在乎。昨天同事要求把百分比結果保存到文件上,而後就以保存1位小數的形式給他保存了。html

可是今天同事運行時問能不能統一以一位小數的形式保存,當時以爲很奇怪昨天就是以一位小數形式存的怎麼還會提這種要求呢。python

其給回的截圖確實是部分是一位小數的,但一部分是很長的。查看代碼都統一以下格式:編碼

# 使用round保留三位小數,而後乘以100,最後格式化爲帶百分號的字符串
rate=f"{round(x/y,3) * 100}%"

代碼上沒看出什麼問題,直接運行確實是有些結果是一長串的。進行調試發現當x爲37y爲76時即會出現問題,以下圖所示:spa

進行步驟拆分,發現round方法沒有問題,問題在浮點數乘以100上(同時以下圖能夠看到也不是全部浮點數乘都有問題)3d

 

2、緣由探究

搞不清緣由,直到看到這篇文章:https://www.programiz.com/python-programming/numbers調試

大意是說二進制對不少浮點數沒法準確表示只能用一個近似值代替,而當使用這些以近似值代替的浮點數進行進算時本質上是這些進似值參與了運算,出來的結果也就是進似值運算後的結果。code

也就是說,一是這不是乘100的問題也不是乘法的問題而是整個浮點數運算都有問題,二是這不是python的問題是計算機浮點數存儲的問題像C、Java等其餘計算機語言進行運算都會有問題。htm

可能有人會疑惑:爲何二進制能夠表示2不能表示0.2呢?blog

這是由於數值和字符串是不同的,若是是字符串那麼表示2.2點的左右兩邊的2編碼是同樣的就能夠了(如ASCII碼:504650),但數值不是這樣,數值的整數部分和小數部分須要一個統一的表示形式,那就是加權位計數法。ci

整數部分都要以2的0次方(20)到2的無窮次方(2)表示,這沒有問題,只要長度足夠就能表示出全部奇數和偶數。2 = 1 * 21 + 0 * 20 = 10

小數部分都要以2的-1次方(2-1)到2的負無窮次方(2-∞)表示,這就有問題,由於好比2-1...2-∞無論怎麼組合都不能徹底等於0.2。0.2 = 0 * 2-1 + 0 * 2-2 + 1 * 2-3 ...

 

3、處理辦法

這狀況讓我想起上份工做局方領導的一句話,應該是「能夠理解但不能接受」。

原理上二進制沒法精確表示一些浮點數能夠理解,可是就這麼返回個顯然錯誤的結果給用戶那是沒法接受的。

python提供了Decimal()方法讓浮點運算結果能夠和人平時運算的結果同樣。(Decimal本質應該仍是經過加長長度提升精度)

# Decimal傳字符串才能準確表示,因此須要先用str()把round()的結果轉爲字符串
rate=f"{Decimal(str(round(x/y,3))) * 100}%"

# 其實上邊的結果出來是48.700%的形式,即三位小數的形式並不太符合咱們保留一位小數的想法,真正符合想法得下面這樣
# rate=f"{round(Decimal(str(round(x/y,3))) * 100, 1)}%"

# 其實咱們說了這麼多,咱們都是創建在決定保留多少位再乘100這個前提下,假若咱們先乘100後決定保留幾位那都不須要用Decimal
# rate=f"{round(x/y*100,1)}%"

 

參考:

https://www.programiz.com/python-programming/numbers

https://docs.python.org/3.7/library/decimal.html

https://baike.baidu.com/item/%E5%8D%81%E8%BF%9B%E5%88%B6%E8%BD%AC%E4%BA%8C%E8%BF%9B%E5%88%B6/393189?fr=aladdin

相關文章
相關標籤/搜索