对我之前的评论进行详细说明:
在Python中,十进制数字就是你可能手写下来的数字类型。重要的是,它不理解递归的概念,因此像1 / 3
这样的无限循环小数只能表示为0.333333333333..
,直到达到你设定的精度为止。当这个数乘以3时,你会得到0.99999..
- 这是合理的行为,因为它实际上不能知道那个被截断的0.33333333..
是1 / 3
。由于四舍五入的原因,Decimal
经常在除法时失去精度(事实上,当你除以任何除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
一个分数将自动简化。
对于您的浮点数,我假设只是幸运的是舍入误差已经抵消了。请注意,除非需要绝对精度,否则
float
或
Decimal
可能已经足够好了,在这种情况下,我建议使用
Fraction
(例如,这意味着您可以可靠地进行等式测试)。您总可以四舍五入一个丑陋的数字,使它看起来至少有点漂亮:
In [4]: "{:.2f}".format(Decimal(3) * (Decimal(1) / Decimal(3)))
Out[4]: '1.00'
如果你正在做模拟之类的事情,0.9999999
和1
之间的差异通常并不重要。
另一个选择是重新排列操作顺序,以便分子能够被分母整除,就像Anilkumar的答案所示。如果可能的话,这是一个好的解决方案,但也许有时候你无法这样做——例如,你期望结果是分数,或者你从某种黑盒子中获取了分数乘数。此时,可以跟踪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
足以满足要求的想法上。如果你真的想要某种形式的根式输出格式,可以尝试使用 sympy
:
In [1]: from sympy import *
In [2]: sqrt(Integer(1) / Integer(2))
Out[2]: sqrt(2)/2
这当然会让你变得更加缓慢。