Python十进制数比较

13

Python十进制比较

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

我原本期望它能正确地转换2.0,但是在阅读PEP 327后,我理解了不将浮点数隐式转换为Decimal的一些原因,但在这种情况下,它不应该像现在这样引发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


在Python 3.8中,Decimal(1.0) > 2.0返回False,这也是我对2.x的预期。 - guidot
3个回答

26

对于问题1,它确实是我们设计的行为 - 不管对错(如果这给您带来了麻烦,我们表示抱歉,但我们试图使其更通用!)。

具体来说,长期以来,每个Python对象都可以被与其他任何对象进行不等比较 - 类型不真正可比较的对象将被随意比较(在给定运行中保持一致,但不一定跨运行);主要用途是对异构列表进行排序以按类型对元素进行分组。

仅针对复数引入了一个例外,使它们不可与任何东西相比较 - 但那是很多年前的事情了,当时我们有时会漫不经心地破坏完全良好的用户代码。现在,我们在一个主要版本中对向后兼容性更加严格(例如沿着2.*线和3.*线分别进行,尽管在2和3之间允许不兼容性 - 实际上这就是拥有 3.*系列的全部意义,即使在不兼容的方式下,让我们修复过去的设计决策)。

随意比较的结果比它们值得的麻烦更多,导致用户混淆;现在可以轻松地通过使用key = lambda x:str(type(x))参数进行按类型分组,因此在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.*中也永远不会是这样)。
至于第二个问题,您会没问题的 - 不过,可以尝试使用gmpy来通过 Farey 树将浮点数转换为无穷大精度分数,这可能是一个有趣的方法。如果您处理的价格要求精确到不超过几分钱,请使用'%.2f' % x而不是repr(x)!-)
与其使用Decimal的子类,我会使用工厂函数,例如:
def to_decimal(float_price):
    return decimal.Decimal('%.2f' % float_price)

一旦生成,所得到的 Decimal 就是一个完全普通的 Decimal。


2
这个答案有点过时了:在2.7版本中,decimal.Decimalfloat是可以进行数值比较的。 "从2.7版本开始更改:浮点数实例x和十进制实例y之间的比较现在返回基于x和y的值的结果。在早期版本中,对于任何十进制实例x和任何浮点数实例y,x < y都会返回相同(任意)的结果。" - Paul Hankin

3

大于比较运算符默认适用于所有对象,因此可以正常工作。

>>> 'abc' > 123
True
< p > Decimal 之所以正确,仅仅是因为它正确地遵循了规范。规范是否是正确的方法是一个单独的问题。 :)

在处理浮点数时只有正常的警告,简要概括如下:小心边缘情况,例如负零,+/-无穷大和NaN,不要测试相等性(与下一个点相关),并且应该考虑到数学会略微不准确。

>>> print (1.1 + 2.2 == 3.3)
False

2
在Python 3中,'abc'> 123会引发TypeError异常。在我看来,这是正确的做法。 - Tim Pietzcker

1

如果它是“正确的”是一个观点问题,但为什么没有自动转换存在于PEP中的理由,这就是所做出的决定。警告基本上是你不能总是精确地在浮点数和十进制数之间进行转换。因此,转换不应该是隐式的。如果您在应用程序中知道您从未有足够的有效数字来影响您,那么允许这种隐式行为的类不应该是一个问题。

另外,一个主要的论点是现实世界中不存在使用情况。如果你到处都使用Decimal,那么很可能会更简单。


我稍微修改了第一个问题,我的意思是如果无法转换,则不会引发异常。 - Anurag Uniyal
啊哈,我明白了。是的,这有不同的理由,我同意抛出异常似乎更加明确。但再次强调,这是个人口味问题。 - Lennart Regebro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接