众所周知,由于四舍五入和精度问题,比较浮点数是否平等有点过分。
例如: https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
在Python中处理这个问题的推荐方法是什么?
当然,这个地方有一个标准的库函数吗?
答案 0 :(得分:245)
Python 3.5添加了math.isclose
and cmath.isclose
functions中描述的PEP 485。
如果您使用的是早期版本的Python,则documentation中会提供等效函数。
def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
rel_tol
是相对容差,它乘以两个参数的大小越大;随着值越大,它们之间允许的差异也越大,同时仍然认为它们相等。
abs_tol
是绝对容差,在所有情况下都按原样应用。如果差异小于这些公差中的任何一个,则认为这些值相等。
答案 1 :(得分:57)
以下简单的事情不够好吗?
return abs(f1 - f2) <= allowed_error
答案 2 :(得分:36)
我同意Gareth的答案可能最适合作为轻量级的功能/解决方案。
但是我认为如果您使用的是NumPy或者正在考虑它会有所帮助,那么就有一个打包的功能。
numpy.isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)
虽然有点免责声明:根据您的平台安装NumPy可能是非常重要的体验。
答案 3 :(得分:12)
使用Python的decimal
模块,该模块提供Decimal
类。
来自评论:
值得注意的是,如果你是 做数学繁重的工作而你却没有 绝对需要精确度 小数,这真的可以搞砸了 下。花车方式,方式更快 处理,但不精确。小数是 非常精确但很慢。
答案 4 :(得分:11)
我不知道实现Dawson的AlmostEqual2sComplement
函数的Python标准库(或其他地方)中的任何内容。如果这是你想要的那种行为,你必须自己实现它。 (在这种情况下,你可能会更好地使用道格if abs(a-b) <= eps1*(abs(a)+abs(b)) + eps2
或类似形式的传统测试,而不是使用道森聪明的按位攻击。要获得类似道森的行为,你可能会说if abs(a-b) <= eps*max(EPS,abs(a),abs(b))
对于一些小的固定EPS
;这与道森不完全相同,但它在精神上是相似的。
答案 5 :(得分:9)
答案 6 :(得分:5)
如果你想在测试/ TDD环境中使用它,我会说这是一种标准方式:
from nose.tools import assert_almost_equals
assert_almost_equals(x, y, places=7) #default is 7
答案 7 :(得分:4)
我发现以下比较有帮助:
str(f1) == str(f2)
答案 8 :(得分:4)
math.isclose()已经added为Python 3.5(source code)。这是Python 2的一个端口。它与Mark Ransom的单行程的区别在于它可以处理&#34; inf&#34;和&#34; -inf&#34;正常。
def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
'''
Python 2 implementation of Python 3.5 math.isclose()
https://hg.python.org/cpython/file/tip/Modules/mathmodule.c#l1993
'''
# sanity check on the inputs
if rel_tol < 0 or abs_tol < 0:
raise ValueError("tolerances must be non-negative")
# short circuit exact equality -- needed to catch two infinities of
# the same sign. And perhaps speeds things up a bit sometimes.
if a == b:
return True
# This catches the case of two infinities of opposite sign, or
# one infinity and one finite number. Two infinities of opposite
# sign would otherwise have an infinite relative tolerance.
# Two infinities of the same sign are caught by the equality check
# above.
if math.isinf(a) or math.isinf(b):
return False
# now do the regular computation
# this is essentially the "weak" test from the Boost library
diff = math.fabs(b - a)
result = (((diff <= math.fabs(rel_tol * b)) or
(diff <= math.fabs(rel_tol * a))) or
(diff <= abs_tol))
return result
答案 9 :(得分:1)
对于某些可以影响源编号表示的情况,可以使用整数分子和分母将它们表示为分数而不是浮点数。这样你就可以进行精确的比较。
有关详细信息,请参阅分数模块中的Fraction。
答案 10 :(得分:1)
我喜欢@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))
答案 11 :(得分:1)
在需要确保2个数字相同且“不超过精度”的情况下很有用,无需指定公差:
查找2个数字的最小精度
将它们都舍入到最低精度并进行比较
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.9999999999995e-4 <数字<= 0.9999999999995e11)
示例:
>>> isclose(10.0,10.049)
True
>>> isclose(10.0,10.05)
False
答案 12 :(得分:0)
这可能是一个有点丑陋的黑客,但是当你不需要超过默认的浮点精度(大约11位小数)时,它的效果非常好。在python 2.7上运行良好。
round_to 函数使用内置str类中的format method将浮点数向上舍入为表示浮点数的字符串需要小数,然后将eval内置函数应用于圆形浮点字符串以返回浮点数字类型。
is_close 函数只是将一个简单的条件应用于向上舍入的浮点数。
def round_to(float_num, decimal_precision):
return eval("'{:." + str(int(decimal_precision)) + "f}'.format(" + str(float_num) + ")")
def is_close(float_a, float_b, decimal_precision):
if round_to(float_a, decimal_precision) == round_to(float_b, decimal_precision):
return True
return False
a = 10.0 / 3
# Result: 3.3333333333333335
b = 10.0001 / 3
# Result: 3.3333666666666666
print is_close(a, b, decimal_precision=4)
# Result: False
print is_close(a, b, decimal_precision=3)
# Result: True
答案 13 :(得分:0)
要比较不带atol/rtol
的给定小数:
def almost_equal(a, b, decimal=6):
return '{0:.{1}f}'.format(a, decimal) == '{0:.{1}f}'.format(b, decimal)
print(almost_equal(0.0, 0.0001, decimal=5)) # False
print(almost_equal(0.0, 0.0001, decimal=4)) # True
答案 14 :(得分:0)
就绝对错误而言,您只需检查
GraphQL
一些信息说明为什么float在Python中表现怪异 https://youtu.be/v4HhvoNLILk?t=1129
您也可以将math.isclose用于相对错误
答案 15 :(得分:-1)
使用 ==
是一个简单的好方法,如果您不关心准确度。
# Python 3.8.5
>>> 1.0000000000001 == 1
False
>>> 1.00000000000001 == 1
True
但要注意0
:
>>> 0 == 0.00000000000000000000000000000000000000000001
False
0
始终为零。
如果您想控制容差,请使用 math.isclose
。
默认 a == b
等价于 math.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
到目前为止,我没有找到任何地方可以为 float
全局配置它。此外,mock
也不适用于 float.__eq__
。