我注意到Python中的数学运算不像以前那么精确,尤其是涉及浮点数的运算。我知道这是由于二进制数表示的性质造成的,我们可以通过这样做来解决这个问题:
from decimal import Decimal
a = Decimal('0.1') + Decimal('0.2')
我什至可以做进一步的事情,例如:
def func(a, b, operator):
a_ = Decimal('{}'.format(a))
b_ = Decimal('{}'.format(b))
return eval('float(a_ {} b_)'.format(operator))
func(0.1, 0.2, '+') # will return 0.3
但是,我不想走这么远。事实上,我一直在使用 python 作为计算器或 Matlab 替代品。为了快速计算而必须编写更多的东西并不方便。 Decimal 模块的上下文设置也需要在数字前面写上“Decimal”。
这个 5 年前的问题 重点关注脚本而不是解释器内部的工作。我还尝试了代码示例,但它没有按预期工作。
是否有一种快速而肮脏的方法可以使 python 执行
0.1 + 0.2
具有与 float(Decimal('0.1') + Decimal('0.2'))
相同的结果?
它也应该应用于其他数学运算,如 **
和相等比较,如 ==
。
一种方法是创建一个名为
D
(十进制)的新对象类型。
from decimal import Decimal
class D:
def __init__(self, d: float | int):
self.d = Decimal(str(d))
def __add__(self, other_d: D):
return self.d + other_d.d
通过在
__add__
上覆盖 D
方法,您可以重新创建您想要的加法行为。同样的方式,你可以覆盖每个算术运算符对应的魔术方法。 (例如,将 __eq__
覆盖为 ==
等)
一旦就位,您可以执行以下操作:
a = D(0.1)
b = D(0.3)
c = a + b
print(c)
-> 0.4 (Decimal type)
IPython 有 hooks 用于在执行前遍历代码的 AST,因此您可以访问任何浮点数,并将其转换为十进制(也转换整数,因此 1/3 将是十进制):
import ast
from decimal import Decimal
class ReplaceFloatWithDecimal(ast.NodeTransformer):
def visit_Constant(self, node):
if isinstance(node.n, (float, int)):
return ast.Call(func=ast.Name(id='Decimal', ctx=ast.Load()),
args=[ast.Call(func=ast.Name(id='str', ctx=ast.Load()),
args=[node], keywords=[])],
keywords=[])
return self.generic_visit(node)
get_ipython().ast_transformers.append(ReplaceFloatWithDecimal())
(在 IPython shell 中执行)
为了使解决方案完整,您可能需要打印没有冗余的数字
Decimal('...')
:
def decimal_formatter(s, printer, cycle):
printer.text(str(s))
get_ipython().display_formatter.formatters['text/plain'].for_type(Decimal, decimal_formatter)
现在你有:
In [2]: 0.1 + 0.2
Out[2]: 0.3
附注这些使用 python 作为计算器的调整(以及更多)在我的包calcpy中使用。