如何检查浮点数列表是否相加等于整数?

4
我想计算一组浮点数的总和,并检查它是否为整数:
l = [0.85, 0.85, 0.15, 0.15]

l的总和为2.0,这显然是正确的。但是由于浮点数精度限制,Python并不认同:

> sum(l)
1.9999999999999998

因此,我选择的方法sum(l).is_integer()将返回False
有什么更好的方法来评估列表是否总和为整数?

3
你知道浮点数的限制吗? - Paritosh Singh
https://dev59.com/G3RB5IYBdhLWcg3wj36c - Paritosh Singh
1
这似乎是一个重复的问题:https://dev59.com/dWEi5IYBdhLWcg3wJZZe - Dani Mesejo
@EricPostpischil 怎么样?他基本上知道如何求和,所以他需要的是如何检查结果是否为整数。被标记的答案基本上告诉了 OP 他所需的内容。 - Dani Mesejo
2
@RaySteam:这里的问题不是确定一个数字是否为整数或在某些范围内找到一个整数,这两个问题都是那个问题中的问题,而是确定一组值的精确数学总和是否为整数。根本问题是在浮点运算引入舍入误差的情况下进行精确计算,这在那个问题中没有得到解决。 - Eric Postpischil
显示剩余2条评论
4个回答

6

您可以使用 decimal 包。

>>> from decimal import Decimal
>>> l = [Decimal(x) for x in ['0.85', '0.85', '0.15', '0.15']]
>>> s = sum(l)
>>> s
Decimal('2.00')
>>> s == int(s)
True

5
需要注意的是,Decimal 是由字符串创建的,而不是从问题中的 float 创建的。 - Mark Ransom

3
您可以使用 math.isclose 与内置的 round 函数的组合来实现。
>>> from math import isclose  
>>> l = [0.85, 0.85, 0.15, 0.15]
>>> s = sum(l)
>>> isclose(s, round(s))
True

这是一个不错的实用解决方案,但请注意,在大型列表(例如 l = [0.05] * 1000000)的情况下会出现问题,并且会产生误报(例如 l = [0.0000000001])。 - Ben Jones
就像我之前所说的,这是一个不错的解决方案,但我想指出这是浮点数的基本限制。更改“rel_tol”只能更改哪些边缘情况会失败。 - Ben Jones

2
当你需要精确计算时,最好的解决方案是仅使用整数。在这种情况下,您可以将每个输入四舍五入到2或3位小数进行检查。
l = [0.85, 0.85, 0.15, 0.15]
number_of_places = 3
multiplier = 10 ** number_of_places
>>> sum(int(round(x*multiplier)) for x in l)
2000
>>> sum(int(round(x*multiplier)) for x in l) % multiplier == 0
True

并非所有情况都适用,但在可能的情况下绝对是一个好方法。 - Mad Physicist

1
除了十进制外,您还可以使用fractions模块,它将使您能够处理任意分母,例如:
from fractions import Fraction
print(Fraction(1,3) + Fraction(4,7) + Fraction(2,21))

也适用于 Fraction('0.85') + Fraction('0.15') 或者 Fraction('2/6')

需要注意的是,将分数相加的结果仍然是一个分数,最终分母可能等于1,因此不能使用 is_integer() 进行测试,而应该使用分母检查或整数转换的相等性进行替代:

collection = [Fraction(each) for each in ['0.85', '0.85', '0.15', '0.15']]
sum = sum(collection)
print(sum.denominator == 1)
print(int(sum) == sum)

在某些语言中,包括Smalltalk,在分母为1时,分数会自动转化为整数,使代码更加健壮...
^((1/3) + (4/7) + (2/21)) isInteger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接