Python Decimal 给出不正确的结果



我在以下计算中遇到了问题:

Decimal(3)*(Decimal(1)/Decimal(3))

它不是返回 1.0,而是返回 0.999...

无论我如何提高Decimal模块的精度,情况仍然如此。

以下是完整的代码:

from decimal import Decimal
from decimal import getcontext
getcontext().prec = 800
print Decimal(3)*(Decimal(1)/Decimal(3))

具有讽刺意味的是,使用"本机"float解决了这个问题:

print float(3)*(float(1)/float(3))

不用说,我在这里使用Decimal进行更复杂的计算,涉及大数的幂。遇到这个问题后,我设法将其最小化到上面的例子。

对我的评论进行一些扩展:

Python 中的十进制数字实际上是您可以手写的数字。重要的是,它不理解递归的概念,所以像1 / 3这样的无限重复的分数只是表示为0.333333333333..到你的精度点。当它乘以 3 时,你会得到0.99999..- 这是明智的行为,因为它实际上无法知道该0.33333333..在被截断后被1 / 3Decimal在除法时经常会因为舍入而失去精度(事实上,当您除以因子不是 2 或 5 的任何值时)。如果能够进行除法至关重要,请使用Fraction,它表示任何有理数,而不会因分子和分母而损失任何精度:

In [1]: from fractions import Fraction
In [2]: Fraction(3) * Fraction(1, 3)
Out[2]: Fraction(1, 1)
In [3]: print(_)
1

分数将自动简化自身。

有了你的浮点数,我认为四舍五入错误已经抵消了自己,这只是运气问题。请注意,除非您需要绝对精度,否则floatDecimal可能就足够了,在这种情况下,我建议您使用Fraction(例如,这意味着您可以可靠地进行相等性测试)。你总是可以对一个丑陋的数字进行四舍五入,让它至少看起来更漂亮一点:

In [4]: "{:.2f}".format(Decimal(3) * (Decimal(1) / Decimal(3)))
Out[4]: '1.00'

如果您正在执行类似模拟的操作,则0.99999991之间的差异通常并不重要。

另一种选择是重新调整操作顺序,以便保证分子可以被分母整除,如 阿尼尔库玛的回答。如果可能的话,这是一个很好的解决方案,但在某些情况下你可能无法做到这一点 - 例如,你期望结果是分数,或者你从某种黑匣子得到分数乘法。此时,可以同时跟踪Decimal分子和分母。然后你意识到这就是Fraction类所做的,但麻烦更少。

请注意,您可以将Fraction用于Decimal可以执行的任何事情,但至关重要的是更多。任何可表示的十进制数也是分数(10的幂上的尾数)。例如:

In [2]: Fraction("3.141")
Out[2]: Fraction(3141, 1000)

当然,这将导致一些性能损失 - 分数必须跟踪更多数据,进行更多计算,并且可能更抽象。

查看您新提供的公式 - 请注意,当您将有理数提高到非整数的幂时,结果可能不是有理数,因此您可能会在此过程中的某个地方丢失分数,例如:

>>> Fraction(1, 2) ** 4
Fraction(1, 16)
>>> Fraction(1, 2) ** 0.5
0.7071067811865476

尽管在评估公式的上下文中,尝试以符号方式存储所有内容没有多大意义。这让我们回到了粗糙float往往足够好的想法。如果你真的想要某种surd输出格式,你可以试一试sympy

In [1]: from sympy import *
In [2]: sqrt(Integer(1) / Integer(2))
Out[2]: sqrt(2)/2

这当然会让你更慢。

我认为偏执狂要改变:

from decimal import Decimal
from decimal import getcontext
getcontext().prec = 800
print (Decimal(3)*Decimal(1))/Decimal(3)

输出对我来说是 1 以上 1

最新更新