Python 舍入问题

Python issues with rounding

我在 Python 2.7 中遇到舍入问题导致意外输出。我试图获得 p1 和 p2 的总和等于或小于 0.6 的组合。

from itertools import product
P = []
p1 = [0.0,0.2,0.4,0.6]
p2 = [0.0,0.2,0.4,0.6] 
for p11,p22 in product(p1,p2):
    if p11+p22 <= max(p1):    
        P.append((p11,p22))

但是,当我 运行 这个时,它不包括 p11+p22 = 0.6 的所有值:

[(0.0, 0.0),
 (0.0, 0.2),
 (0.0, 0.4),
 (0.0, 0.6),
 (0.2, 0.0),
 (0.2, 0.2),
 (0.4, 0.0),
 (0.6, 0.0)]

当我设置 p11+p22 <= max(p1)+0.01 时它确实工作正常。对于不同的 p1p2,问题可能发生也可能不发生。我发现这种行为非常奇怪,导致非常不可靠的结果。

可能与浮动精度问题有关。在我看来,这种行为不应该存在于 Python 中,因为 R 和 Matlab 也没有这种行为。有什么简单的方法可以解决这个问题吗?

一种解决方法是定义不同的不等式比较函数,

function less_or_nearly_equal(a,b)
    return (a <= b + .000001)

并在比较中使用它而不是 <=

for p11,p22 in product(p1,p2):
    if less_or_nearly_equal(p11+p22,max(p1)):    
        P.append((p11,p22))

(希望我的函数定义语法正确。)

因为 limitations 固定位宽浮点数,您需要使用任意精度浮点数包或与 +/- epsilon 量显式比较。

Python 包括 decimal(对于 'arithmetic that works in the same way as the arithmetic that people learn at school'):

from itertools import product
import decimal

P = []
p1 = map(decimal.Decimal, ['0.0','0.2','0.4','0.6'])
p2 = map(decimal.Decimal, ['0.0','0.2','0.4','0.6'])
for p11,p22 in product(p1,p2):
    if p11+p22 <= max(p1):    
        P.append((p11,p22))

发生了什么事?

计算机有数字的内部表示。在大多数情况下,这些表示具有固定的位数。这导致只能表示固定数量的数字。例如,您可能知道像 C 这样的语言对整数有最大值。

类似的,你不能存储一些浮点数的精确表示。由于计算机使用二进制,因此有些以 10 为基数的数字具有短而有限的表示形式,但二进制数却很长。有关详细信息,请参阅 IEEE 754

怎么可能"fixed"?

这里没有什么需要修复的,因为一切都按照指定的方式工作。但是您必须了解这些类型的问题。当您意识到存在问题的事实时,有两种策略可以解决它。

要么使用epsilons(->不要与精确数字进行比较,而是检查数字是否在数字周围的一个非常小的区间内。这个区间的长度是通常称为 "epsilon")或使用任意精度表示(参见 fractions。第二种仅在您可以影响数字放入程序的方式时才有效,例如

from itertools import product
from fractions import Fraction
P = []
p1 = [Fraction(0.0), Fraction(2, 10), Fraction(4, 10), Fraction(6, 10)]
p2 = [Fraction(0.0), Fraction(2, 10), Fraction(4, 10), Fraction(6, 10)]
for p11, p22 in product(p1, p2):
    if p11+p22 <= max(p1):
        P.append((p11, p22))

另见

如果您想与 matlab 或 R 进行比较,或者有性能问题,这里有一个 numpy 方法,np.isclose() 作为解决方法。

p1 = [0.0,0.2,0.4,0.6]
p2 = [0.0,0.2,0.4,0.6] 
sums=np.add.outer(p1,p2)
P1,P2=np.meshgrid(p1,p2)
compare = (sums<0.6) | np.isclose(sums,0.6)
print(np.dstack((P1,P2))[compare])

给出:

[[ 0.   0. ]
 [ 0.2  0. ]
 [ 0.4  0. ]
 [ 0.6  0. ]
 [ 0.   0.2]
 [ 0.2  0.2]
 [ 0.4  0.2]
 [ 0.   0.4]
 [ 0.2  0.4]
 [ 0.   0.6]]