python十进制/浮点中的邪恶

python十进制/浮点中的邪恶,python,floating-point,decimal,python-2.4,Python,Floating Point,Decimal,Python 2.4,我有大量的python代码试图以4位小数的精度处理数字,出于许多原因,我一直使用Python2.4。该代码做了非常简单的数学运算(它是一个信用管理代码,主要用于获取或添加信用) 它混合使用浮点和十进制(MySQLdb返回SQL十进制类型的十进制对象)。在使用过程中出现了几个奇怪的错误之后,我发现所有错误的根本原因是代码中有几个地方比较了浮点和小数 我遇到过这样的情况: >>> from decimal import Decimal >>> max(Decima

我有大量的python代码试图以4位小数的精度处理数字,出于许多原因,我一直使用Python2.4。该代码做了非常简单的数学运算(它是一个信用管理代码,主要用于获取或添加信用)

它混合使用浮点和十进制(MySQLdb返回SQL十进制类型的十进制对象)。在使用过程中出现了几个奇怪的错误之后,我发现所有错误的根本原因是代码中有几个地方比较了浮点和小数

我遇到过这样的情况:

>>> from decimal import Decimal
>>> max(Decimal('0.06'), 0.6)
Decimal("0.06")
现在我担心的是,我可能无法在代码中捕获所有此类情况。(一个普通的程序员会继续执行x>0而不是x>Decimal('0.0000'),这是很难避免的)

我已经想出了一个补丁(灵感来自python 2.7中对decimal包的改进)

我只是在很早的加载库中这样做,它将通过在比较之前允许浮点到十进制的转换来改变十进制包的行为(以避免影响python的默认对象到对象比较)

我特别使用了“str”而不是“repr”,因为它修复了float的一些舍入情况。例如

>>> Decimal(str(0.6))
Decimal("0.6")
>>> Decimal(repr(0.6))
Decimal("0.59999999999999998")
现在我的问题是: 我有什么遗漏吗?这相当安全吗?还是我在这里弄坏了什么?
(我认为该包的作者有非常充分的理由避免如此多的浮动)

我认为您希望
启动raisenotimplementederror()
而不是
returnnotimplemented

你所做的被称为“猴子修补术”,只要你知道你在做什么,知道辐射,并且对辐射没有问题,你就可以这么做。通常情况下,您会将此限制为修复bug,或者进行其他更改,您知道更改的行为仍然正确且向后兼容

在这种情况下,因为您正在修补一个类,所以可以在使用它的情况之外更改行为。如果另一个库使用decimal,并且以某种方式依赖于默认行为,则可能会导致一些微妙的错误。问题是,除非您审核所有代码,包括任何依赖项,并找到所有调用站点,否则您并不真正知道

基本上-风险自负

就我个人而言,修复我所有的代码、添加测试并使做错事变得更加困难(例如,使用包装类或帮助函数)更令人放心。另一种方法是使用补丁插入代码,找到所有呼叫站点,然后返回并修复它们


编辑-我想我应该补充一点,他们避免浮动的可能原因是浮动不能准确地表示所有的数字,这对于处理金钱问题很重要。

避免浮动有很好的理由。使用浮点,由于浮点噪声,您无法可靠地进行比较,例如==、>、<等。任何浮点运算都会累积噪声。它以非常小的数字开始出现在最末尾,例如1.000…002,但它最终可以累积,例如1.0000000453436

如果你不做那么多的浮点计算,使用str()可能对你有用,但是如果你做了很多计算,浮点噪声最终会大到str()会给你错误的答案

总之,如果 (1) 你不需要做那么多的浮点运算,或者 (2) 您不需要像==、>、<等那样进行比较 那你就没事了


如果您想确定,请删除所有浮点代码。

请注意,“return NotImplemented”来自decimal.py包本身。我添加的两行在注释之间。我同意您的方法,但是,在这个实现中,python允许在我们假设都是数字的对象之间进行逻辑上疯狂的比较。嗯,另一个想法可能是引发一个错误而不是隐式转换,但无论如何,我认为我需要做一些事情…
returnnotimplemented
是正确的,并且是正确的,对于不受支持的比较返回的东西。它允许python尝试找到另一种方法来做事情。+1使用术语“猴子补丁”,这让我找到了wikipedia这个术语,发现它来自“游击战补丁”,就像在游击战=)。有很好的理由避免在类似问题中的会计程序中出现浮动。浮点数对于表示近似量的预期目的来说工作得非常好。@Dan,是的,我回答的前提是你不能用浮点数做==运算。如果您表示的是近似量,那么您就不用==了,因为等式不是近似的。
>>> Decimal(str(0.6))
Decimal("0.6")
>>> Decimal(repr(0.6))
Decimal("0.59999999999999998")