众所周知,由于舍入和精度问题,比较浮点数是否相等有点棘手。
例如:比较浮点数,2012版
在Python中处理这个问题的推荐方法是什么?
有标准的库函数吗?
众所周知,由于舍入和精度问题,比较浮点数是否相等有点棘手。
例如:比较浮点数,2012版
在Python中处理这个问题的推荐方法是什么?
有标准的库函数吗?
当前回答
使用==是一个简单的好方法,如果你不关心公差精确。
# Python 3.8.5
>>> 1.0000000000001 == 1
False
>>> 1.00000000000001 == 1
True
但是要注意0:
>>> 0 == 0.00000000000000000000000000000000000000000001
False
0始终是0。
使用数学。如果你想控制公差,是接近的。
默认值a == b等价于数学。Isclose (a, b, rel_tol=1e-16, abs_tol=0)。
如果你仍然想使用==带有自定义容差:
>>> class MyFloat(float):
def __eq__(self, another):
return math.isclose(self, another, rel_tol=0, abs_tol=0.001)
>>> a == MyFloat(0)
>>> a
0.0
>>> a == 0.001
True
到目前为止,我没有找到任何地方配置它全局浮动。此外,mock也不能用于float.__eq__。
其他回答
使用==是一个简单的好方法,如果你不关心公差精确。
# Python 3.8.5
>>> 1.0000000000001 == 1
False
>>> 1.00000000000001 == 1
True
但是要注意0:
>>> 0 == 0.00000000000000000000000000000000000000000001
False
0始终是0。
使用数学。如果你想控制公差,是接近的。
默认值a == b等价于数学。Isclose (a, b, rel_tol=1e-16, abs_tol=0)。
如果你仍然想使用==带有自定义容差:
>>> class MyFloat(float):
def __eq__(self, another):
return math.isclose(self, another, rel_tol=0, abs_tol=0.001)
>>> a == MyFloat(0)
>>> a
0.0
>>> a == 0.001
True
到目前为止,我没有找到任何地方配置它全局浮动。此外,mock也不能用于float.__eq__。
这对于你想要确保两个数字是相同的“达到精度”的情况很有用,并且不需要指定公差:
求这两个数的最小精度 将两者舍入到最小精度并进行比较
def isclose(a, b):
astr = str(a)
aprec = len(astr.split('.')[1]) if '.' in astr else 0
bstr = str(b)
bprec = len(bstr.split('.')[1]) if '.' in bstr else 0
prec = min(aprec, bprec)
return round(a, prec) == round(b, prec)
如上所述,它只适用于字符串表示中没有'e'的数字(意思是0.999999999999995e -4 < number <= 0.99999999999999995e11)
例子:
>>> isclose(10.0, 10.049)
True
>>> isclose(10.0, 10.05)
False
I'm not aware of anything in the Python standard library (or elsewhere) that implements Dawson's AlmostEqual2sComplement function. If that's the sort of behaviour you want, you'll have to implement it yourself. (In which case, rather than using Dawson's clever bitwise hacks you'd probably do better to use more conventional tests of the form if abs(a-b) <= eps1*(abs(a)+abs(b)) + eps2 or similar. To get Dawson-like behaviour you might say something like if abs(a-b) <= eps*max(EPS,abs(a),abs(b)) for some small fixed EPS; this isn't exactly the same as Dawson, but it's similar in spirit.
这可能是一个有点丑陋的hack,但当你不需要超过默认的浮点精度(大约11个小数)时,它工作得很好。
round_to函数使用内置str类中的format方法将浮点数四舍五入为表示浮点数的字符串,其中包含所需的小数数,然后将eval内置函数应用于四舍五入的浮点数字符串,以返回浮点数字类型。
is_close函数只是对四舍五入的浮点数应用一个简单的条件。
def round_to(float_num, prec):
return eval("'{:." + str(int(prec)) + "f}'.format(" + str(float_num) + ")")
def is_close(float_a, float_b, prec):
if round_to(float_a, prec) == round_to(float_b, prec):
return True
return False
>>>a = 10.0
10.0
>>>b = 10.0001
10.0001
>>>print is_close(a, b, prec=3)
True
>>>print is_close(a, b, prec=4)
False
更新:
正如@stepehjfox所建议的,构建一个避免“eval”的rount_to函数的更干净的方法是使用嵌套格式:
def round_to(float_num, prec):
return '{:.{precision}f}'.format(float_num, precision=prec)
遵循同样的思想,使用新的f-string (Python 3.6+)代码可以更简单:
def round_to(float_num, prec):
return f'{float_num:.{prec}f}'
所以,我们甚至可以用一个简单干净的'is_close'函数来概括它:
def is_close(a, b, prec):
return f'{a:.{prec}f}' == f'{b:.{prec}f}'
我喜欢Sesquipedal的建议,但有修改(一个特殊的用例时,两个值都是0返回False)。在我的例子中,我使用的是Python 2.7,只使用了一个简单的函数:
if f1 ==0 and f2 == 0:
return True
else:
return abs(f1-f2) < tol*max(abs(f1),abs(f2))