16

我有大量的 python 代码试图处理具有 4 位小数精度的数字,并且由于多种原因我被 python 2.4 卡住了。该代码进行了非常简单的数学运算(它是一个信用管理代码,主要用于获取或添加信用)

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

我遇到过这样的情况:

>>> from decimal import Decimal
>>> max(Decimal('0.06'), 0.6)
Decimal("0.06")

现在我担心的是我可能无法在代码中捕获所有此类情况。(一个普通的程序员会继续做 x > 0 而不是 x > Decimal('0.0000') 并且很难避免)

我想出了一个补丁(灵感来自对 python 2.7 中十进制包的改进)。

import decimal
def _convert_other(other):
     """Convert other to Decimal.

     Verifies that it's ok to use in an implicit construction.
     """
     if isinstance(other, Decimal):
         return other
     if isinstance(other, (int, long)):
         return Decimal(other)
     # Our small patch begins
     if isinstance(other, float):
         return Decimal(str(other))
     # Our small patch ends
     return NotImplemented
decimal._convert_other = _convert_other

我只是在一个非常早的加载库中执行此操作,它将通过允许在比较之前将浮点数转换为十进制来改变十进制包的行为(以避免遇到 python 的默认对象到对象比较)。

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

>>> Decimal(str(0.6))
Decimal("0.6")
>>> Decimal(repr(0.6))
Decimal("0.59999999999999998")

现在我的问题是:我在这里遗漏了什么吗?这相当安全吗?还是我在这里打破了什么?(我认为该软件包的作者有非常充分的理由来避免如此多的浮动)

4

2 回答 2

3

我想你想raise NotImplementedError()而不是return NotImplemented, 开始。

你正在做的事情被称为“猴子补丁”,只要你知道自己在做什么,知道后果,并且可以接受后果,就可以这样做。通常,您将其限制为修复错误或其他一些您知道您正在更改的行为仍然正确且向后兼容的更改。

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

基本上 - 风险自负。

就我个人而言,我发现修复我的所有代码、添加测试并让做错事变得更加困难(例如,使用包装类或辅助函数)更让人放心。另一种方法是使用补丁检测您的代码以查找所有调用站点,然后返回并修复它们。

编辑 - 我想我应该补充一点,他们避免使用浮点数的可能原因是浮点数不能准确地表示所有数字,如果你在处理金钱,这很重要。

于 2010-11-15T08:24:02.997 回答
3

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

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

总而言之,如果(1)你不需要做那么多浮点计算,或者(2)你不需要像 ==、>、< 等进行比较,那么你可能没问题。

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

于 2010-11-15T14:06:46.900 回答