我刚刚遇到一个问题,我需要将数字附加到列表中,只要它们不在列表中,然后我必须通过比较来运行这些数字。问题出现在浮点算术错误中。为了说明我的代码中基本上发生了什么:
_list = [5.333333333333333, 6.666666666666667, ...]
number = some_calculation()
if number not in _list:
_list.append(number) #note that I can't use a set to remove
#duplicates because order needs to be maintained
new_list = []
for num in _list:
if some_comparison(num): #note that I can't combine 'some_comparison' with the
new_list.append(num) #above check to see if the item is already in the list
问题是some_calculation()
有时会生成一个不精确的数字,例如5.333333333333332,就我的计算需要而言,这与本例中_list
中的第一个元素相同。我想到的解决方案是将生成的所有数字简化为小数点后9位左右。这工作了很短的时间,直到我意识到some_comparison
再次将num
与不精确的计算进行比较。即使我没有对_list
中的数字进行舍入,some_comparison
仍然会返回一个不精确的值,因此会评估为False。
我绝对感到困惑。我从来不必担心浮点错误,所以这个问题非常令人愤怒。有没有人对解决方案有任何想法?
注意:我会发布实际的代码,但它非常复杂,需要7或8个不同的函数和类我专门为此目的而制作,并将它们重新发布在这里会很麻烦。
答案 0 :(得分:1)
进行比较
if(abs(a-b) <= 1e-6 * (a + b)):
这是使用浮点时的标准做法。您使用的实际值(而不是1e-6)取决于您使用的数字的大小以及您对#34;相同的&#34;的定义。
编辑我添加了*(a+b)
为不同幅度的值提供了一些稳健性,并将比较更改为<=
而非<
以涵盖a==b==0.0
。
答案 1 :(得分:1)
您可以继承list
并为__contains__
添加容差:
class ListOFloats(list):
def __contains__(self, f):
# If you want a different tolerance, set it like so:
# l=ListOFloats([seq])
# l.tol=tolerance_you_want
tol=getattr(self, 'tol', 1e-12)
return any(abs(e-f) <= 0.5 * tol * (e + f) for e in self)
_list = ListOFloats([5.333333333333333, 6.666666666666667])
print(5.333333333333333 in _list)
# True
print(6.66666666666666 in _list)
# True
print(6.66666666666 in _list)
# False
答案 2 :(得分:0)
对列表中的值和比较值使用round
。他们不会准确,但他们会保持一致,所以搜索会返回预期的结果。