Python中的舍入误差

3
为什么乘法的顺序会影响结果?考虑以下代码。
a=47.215419672114173
b=-0.45000000000000007
c=-0.91006620964286644
result1=a*b*c
temp=b*c
result2=a*temp
result1==result2

我们都知道结果1应该等于结果2,但是我们得到的是:
result1==result2 #FALSE!

差异很小。
result1-result2 #3.552713678800501e-15

然而,对于特定的应用程序,误差可能会放大,以至于进行相同计算的两个程序的输出(一个使用result1,另一个使用result2)可能完全不同。这是为什么?在数字/科学应用程序中如何解决这些问题?谢谢!
更新:好的回答,但我仍然想知道为什么乘法的顺序很重要,例如。
temp2=a*b
result3=temp2*c
result1==result3 #True

看起来编译器/解释器把a*b*c视为(a*b)*c。


3
请查看Decimal模块 - Joel Cornett
9
将机器表示的浮点数进行精确相等比较会让宇宙不太愉快,我的朋友。 - Keith Flower
3
@Mannaggia - 这些链接或许对你理解你所注意到的行为会有帮助:浮点数陷阱浮点数算术:问题与限制 - Keith Flower
2
@Marcin:好的,但是如果一个人只能在他/她精通的领域提出问题,那么这个博客的作用是什么?我的问题是关于操作的顺序以及它如何影响精度,我知道实数不能用二进制形式表示,这与任何IEEE标准无关(这只是一种约定),而是与二进制(或三进制,如果你愿意)编码系统有关。 - Mannaggia
2
@Marcin 他最初的问题是一些教科书上没有列出的内容——如果他知道在哪里找到这样的信息的话——因为他的问题是为什么一组他认为执行相同硬件操作的计算结果略有不同。回答需要了解操作顺序和硬件不准确性的知识。对我来说,这似乎是一个非常合理的问题。 - Pyrce
显示剩余4条评论
9个回答

9
所有编程语言在将浮点数从十进制转换为二进制表示时都会失去精度。这导致计算不准确(至少从十进制的角度来看,因为实际上是在二进制中表示浮点值进行计算),包括操作顺序改变结果的情况。大多数语言提供了一种数据结构来保持十进制精度,但代价是性能下降。请查看Python中的Decimal
编辑:
回答您的更新,不完全正确。计算机按顺序执行操作,因此当您提供一系列操作时,它们会逐个通过该序列进行处理。除了顺序命令处理外,没有显式的操作顺序问题。

作为浮点数误差的一个例子,尝试 1-0.9。结果是 0.09999999999999998,而不是 0.1 - MartinHaTh
1
这会导致计算结果不精确。说到点上,我不会说计算是不准确的,而只能说它们只是实数计算的近似值。 - Marcin
@sr2222 实际上更好。 - Marcin

6
当你在任何编程语言中使用浮点数时,都会损失精度。你可以采取以下两种方式之一:
为精度损失提供适配,并相应地调整你的等式检查,如下所示:
 are_equal = (result1-result2)>0.0001

这里的0.0001(epsilon)是您设定的值。

或者使用Python提供的Decimal类,但它会稍微慢一些。


4
针对展示了在99%的情况下应该如何进行浮点数等值检查,我给出+1的赞同。 - Pyrce

6
每次乘法都会产生比原始数字两倍多的数字(或位数),需要四舍五入以适应分配给浮点数的空间。当重新排列顺序时,这种四舍五入可能会改变结果。

谢谢,这个回答从概念上回答了关键问题,同时也感谢@sr2222的贡献,可惜只能有一个正确答案 :-) - Mannaggia
1
@Mannaggia,即使值可以在二进制中准确表示,我的答案仍然是正确的。 - Mark Ransom
是的,因为当计算结果放回内存时,由于比特数减少而导致精度丢失。 - Mannaggia
1
@Mannaggia,这与内存无关,而是处理器本身可用的位数。例如,在x86上,浮点值可以在有效数字中保持53位,但乘法会产生106位。内部寄存器可以容纳64位有效数字,但仍然不足,结果必须在写入内存之前进行舍入。 - Mark Ransom

3

浮点数比较应该始终使用小于10的幂次方的 epsilon 进行(由您)比较。


1
我们都知道result1应该等于result2,但是我们得到了:
不,我们并不都知道。事实上,它们不应该相等,这就是它们不相等的原因。
你似乎认为你正在使用实数。你不是——你正在使用IEEE浮点表示。它们不遵循相同的公理。它们不是同一件事。
操作的顺序很重要,因为Python评估每个表达式,这会导致一个浮点数。

1
当然,我是指在符号上这两个表达式是相等的。 - Mannaggia
1
@Mannaggia 不,从符号上看这两个表达式是不相等的。这就是它们不相等的原因。再次强调:你并不是在处理实数。 - Marcin
那么为什么这是正确的:temp2=ab result3=temp2c result1==result3 - Mannaggia
1
@Mannaggia 这是真的,因为这些计算得出了相同的结果。仅仅因为一个计算按照你认为正确的方式工作,并不意味着所有的计算都应该按照你认为正确的方式工作。再次强调,这些不是实数,对它们的操作并不遵循相同的法则。 - Marcin
1
仅仅因为一些浮点数操作是可交换的,并不意味着所有操作都是可交换的。在处理浮点数时,可交换行为是一个目标,而不是保证。 - Silas Ray

1

1

在计算机科学中,用计算机表示数字是一个重要的研究领域。这不仅是Python中存在的问题,任何编程语言都有这个特性,因为默认情况下执行任何精确计算都会非常昂贵。

算法的数值稳定性反映了在思考数值算法时的一些限制。如前所述,十进制被定义为在银行应用程序或任何可能需要它的应用程序中执行精确计算的标准。在Python中,有一个实现这个标准的方法。


0

这里有一些关于如何处理浮点数算术的好答案。但是你似乎更具体地问了为什么 a*b*c != b*c*a [result1 != result2]。答案很简单:浮点算术不保证是可结合的。

当你赋值 temp = b*c 时,计算机已经进行了不精确的计算(因为它截断了),并且误差传播到了 result2 = a*temp。 另一方面,当你计算 Result1 = a*b*c 时,误差从中间结果 a*b 开始,并传播到 *b。例如,如果你将数字0.11加10k次,你会比将0.11 * 10k相乘产生更多的不精确性,因为误差在许多操作中传播。

如果您想要更深入了解此主题,可以阅读Python浮点数文档、文章计算机科学家应知的浮点数算术概念或任何关于数值分析/方法的介绍,这些资料在许多课程/书籍中都有提供。

0

正如之前的答案所述,这是编程语言中常见的浮点数算术问题。 您应该注意永远不要对 float 类型应用精确相等性。

当您进行此类比较时,可以使用基于给定公差(阈值)的函数进行比较。 如果数字足够接近,则应将它们视为数字相等。 像这样:

def isequal_float(x1,x2, tol=10**(-8)):
    """Returns the results of floating point equality, according to a tolerance."""
    return abs(x1 - x2)<tol

会起作用。如果我没记错的话,精度取决于您使用的语言以及float类型是单精度还是双精度。

使用这样的函数可以轻松比较计算结果,例如在numpy中。让我们以以下示例为例,其中对具有连续变量的数据集计算相关矩阵,使用两种方法: pandas方法pd.DataFrame.corr()numpy函数np.corrcoef()

import numpy as np
import seaborn as sns 

iris = sns.load_dataset('iris')
iris.drop('species', axis = 1, inplace=True)

# calculate correlation coefficient matrices using two different methods
cor1 = iris.corr().to_numpy()
cor2 = np.corrcoef(iris.transpose())

print(cor1)
print(cor2)

结果看起来相似:

[[ 1.         -0.11756978  0.87175378  0.81794113]
 [-0.11756978  1.         -0.4284401  -0.36612593]
 [ 0.87175378 -0.4284401   1.          0.96286543]
 [ 0.81794113 -0.36612593  0.96286543  1.        ]]
[[ 1.         -0.11756978  0.87175378  0.81794113]
 [-0.11756978  1.         -0.4284401  -0.36612593]
 [ 0.87175378 -0.4284401   1.          0.96286543]
 [ 0.81794113 -0.36612593  0.96286543  1.        ]]

但它们的精确相等性结果并不相同。这些运算符:

print(cor1 == cor2)
print(np.equal(cor1, cor2))

将会逐元素产生大多数False结果:

[[ True False False False]
 [False False False False]
 [False False False False]
 [False False False  True]]

同样地,np.array_equal(cor1, cor2)也会产生False。然而,自定义函数提供了您想要的比较:

out = [isequal_float(i,j) for i,j in zip(cor1.reshape(16, ), cor2.reshape(16, ))]
print(out)

[True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True]

注意:numpy 包含 .allclose() 函数,可在 numpy 数组中执行浮点元素逐个比较。

print(np.allclose(cor1, cor2))
>>>True

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