13

python十进制比较

>>> from decimal import Decimal
>>> Decimal('1.0') > 2.0
True

我期待它能够正确转换 2.0,但是在阅读PEP 327之后,我明白有一些原因不会将浮点数隐式转换为十进制,但在这种情况下它不应该像在这种情况下那样引发 TypeError

>>> Decimal('1.0') + 2.0
Traceback (most recent call last):
  File "<string>", line 1, in <string>
TypeError: unsupported operand type(s) for +: 'Decimal' and 'float'

所有其他运算符也是如此 / - % // 等等

所以我的问题是

  1. 这是正确的行为吗?(不要在 cmp 中引发异常)
  2. 如果我派生自己的类并正确使用浮点转换器,基本上是 Decimal(repr(float_value)),有什么注意事项吗?我的用例只涉及价格比较

系统详细信息:Ubuntu 8.04.1 上的 Python 2.5.2

4

3 回答 3

26

Re 1,这确实是我们设计的行为——它可能是对还是错(对不起,如果这会让你的用例出错,但我们试图笼统地讲!)。

具体来说,长期以来,每个 Python 对象都可能与其他对象进行不等式比较——无法真正比​​较的类型的对象会被任意比较(在给定的运行中始终如一,不一定跨运行);主要用例是对异构列表进行排序以按类型对其中的元素进行分组。

只为复数引入了一个例外,使它们无法与任何东西相提并论——但这仍然是很多年前的事了,那时我们偶尔会粗心地破坏完美的用户代码。如今,我们对主要版本中的向后兼容性更加严格(例如,沿线2.*,以及沿线分开3.*,尽管在 2 和 3 之间允许不兼容-实际上这就是拥有系列的全部意义3.*,让我们修复过去的设计甚至以不相容的方式做出决定)。

随意的比较结果比它们的价值更麻烦,导致用户困惑;现在可以很容易地获得按类型分组,例如key=lambda x: str(type(x))使用sort; 因此,在 Python 3 中,不同类型的对象之间的比较,除非对象本身在比较方法中明确允许,否则会引发异常:

>>> decimal.Decimal('2.0') > 1.2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: Decimal() > float()

换句话说,在 Python 3 中,它的行为与您认为的完全一样;但在 Python 2 中它不会(并且永远不会在任何 Python 中2.*)。

Re 2,你会没事的——不过,看看gmpy,我希望这是一种有趣的方法,可以通过 Farey 树将双精度数转换为无限精度分数。如果您处理的价格精确到不超过美分,请使用'%.2f' % x而不是repr(x)!-)

我不会使用 Decimal 的子类,而是使用工厂函数,例如

def to_decimal(float_price):
    return decimal.Decimal('%.2f' % float_price)

因为一旦生成,生成的 Decimal 就是一个完全普通的 Decimal。

于 2009-06-30T06:26:26.187 回答
3

大于比较有效,因为默认情况下,它适用于所有对象。

>>> 'abc' > 123
True

Decimal是正确的,仅仅是因为它正确地遵循了规范。规范是否是正确的方法是一个单独的问题。:)

处理浮点数时只有正常的注意事项,简要总结为:注意负零、+/-无穷大和 NaN 等边缘情况,不要测试相等性(与下一点相关),并依靠数学稍微不准确。

>>> print (1.1 + 2.2 == 3.3)
False
于 2009-06-30T06:25:40.460 回答
1

如果它“正确”是一个见仁见智的问题,但是 PEP 中存在为什么没有自动转换的理由,这就是做出的决定。需要注意的是,您不能总是在浮点数和小数之间精确转换。因此转换不应该是隐式的。如果您在应用程序中知道您永远不会有足够的有效数字来影响您,那么创建允许这种隐式行为的类应该不是问题。

此外,一个主要论点是现实世界的用例不存在。如果您只是在任何地方使用 Decimal,它可能会更简单。

于 2009-06-30T06:24:55.097 回答