一日一技:在 Python 裡面,如何正確判斷兩個浮點數相等
- 2019 年 12 月 16 日
- 筆記

攝影:產品經理
下廚:產品經理
在以前的文章裡面,我們已經講到過,不僅僅是 Python,很多程式語言裡面,浮點數都不一定是精確的。最常被用來作為例子的是:0.1 + 0.2。在 Python 裡面,這個加法的結果如下圖所示:

因為這個原因,我們顯然不能直接用等號判斷兩個浮點數是否相等,如下圖所示:

在工程上,我們不要求絕對精確,只要求足夠精確就可以了。所以,當我們要判斷兩個數是否相等時,只需要判斷這兩個數的差值是否小於某一個特定的值即可。例如:
0.30000000000000004 0.3
它們的差值是0.00000000000000004,(十億億分之4),無論是測量重量還是測量長度,這個差異都可以忽略不計,因此可以認為0.30000000000000004與0.3在誤差範圍內是相等的。
如果大家去網上搜索某些判斷浮點數是否相等的方法,你可能會發現下面這種寫法:
def compare_float(a, b, precision): if abs(a - b) <= precision: return True return False
看起來似乎沒有問題,例如我把 precision 設置為,這不就是判斷a,b這兩個數字的差值的絕對值小於嗎?如果差值的絕對值小於這麼小的一個精度值,不就說明可以判斷他們相等嗎?
但是等等,在程式碼裡面,有一個<=,這個符號的意思是小於或者等於,有等於在裡面,所以if abs(a - b) <= precision等價於if abs(a - b) < precision or abs(a - b) == precision。
但是我們一開始就說明了,兩個浮點數不能使用==來判斷是否相等,所以這裡abs(a - b) == precision得到的結果本來就是不準確的,怎麼可以用不準確的結果來說明另一個不準確的結果?
所以,正確的做法,應該是把兩邊都擴大很多倍,讓他們都變成整數再來對比。
例如我設置的精度為,那麼我們就把兩邊同時乘以10^9:
if 1e9 * abs(a - b) <= 1: return True
所以比較浮點數是否相等的程式碼應該改為:
def compare_float(a, b, precision): if precision == 0: return a == b elif precision < 0: raise Exception('precision 不能小於0') elif precision >= 1: if abs(a - b) <= precision: return True else: if (1 / precision) * abs(a - b) <= 1: return True return False 當精度設定為0的時候,就是要求 a 和 b絕對相等,不能有誤差,此時可以使用==來進行判斷。如果precision大於1,那麼此時可以直接判斷。如果0 < precision < 1,那麼兩邊乘以,把浮點數對比轉換為整數對比。
在 Python 3裡面,我們有現成的庫math.isclose,它的用法如下:
>>> import math >>> math.isclose(0.1 + 0.2, 0.3) True
運行效果如下圖所示:

math.isclose默認的誤差值是,你也可以增加一個參數人工指定:
>>> math.isclose(0.1 + 0.2, 0.3, rel_tol=1e-5)
這裡設定誤差值為。

