一日一技:在 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)  

這裡設定誤差值為。

Tags: