Skip to content


Comparing floating-point and decimal in Python

Bizarre happenings in the world of Python. It seems that you are really not supposed to compare floating-point and decimal numbers, as this example shows:

Python 2.5.1 (r251:54863, Jan 17 2008, 19:35:17)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from decimal import Decimal
>>> Decimal('1.0') > 2.0
False
>>> Decimal('3.0') > 2.0
False
>>> Decimal('1.0') < 2.0
True
>>> Decimal('3.0') < 2.0
True
>>> 2.0 < Decimal('1.0')
False
>>> 2.0 < Decimal('3.0')
False
>>> 2.0 > Decimal('1.0')
True
>>> 2.0 > Decimal('3.0')
True

But that’s not the end yet. I have NLTK 0.9.4 installed, and watch how truth is reversed if I simply import the NLTK package:

>>> import nltk
>>> Decimal('1.0') > 2.0
True
>>> Decimal('3.0') > 2.0
True
>>> Decimal('1.0') < 2.0
False
>>> Decimal('3.0') < 2.0
False
>>> 2.0 < Decimal('1.0')
True
>>> 2.0 < Decimal('3.0')
True
>>> 2.0 > Decimal('1.0')
False
>>> 2.0 > Decimal('3.0')
False

I’m filing bugs about this, but I do find this quite entertaining in a ‘oh-my-God-I-always-believed-Python-was-a-well-behaved-language’ sort of way.

Edit: Edward Loper from NLTK gave an explanation why this is the case — it’s not because of NLTK, but because of Python’s internal handling of the comparison operator on floating-point numbers:

Apparently, you’re only allowed to use comparison operators to compare Decimal objects to (i) other Decimal objects; (ii) integers; (iii) longs. Here, you’re comparing it to a float, which isn’t allowed, as evidenced by the following:

>>> Decimal('.1').__cmp__(1)
-1
>>> Decimal('.1').__cmp__(.3)
NotImplemented

Since Decimal’s __cmp__ method returns NotImplemented, python falls back on using .3‘s __cmp__ method. Unfortunately, when you compare a float to some random object, the results are pretty much arbitrary, and are not guaranteed to be consistent. [...]

nltk’s not really playing much of a role here (other than tweaking the system to change that arbitrary result — my guess would be that the result depends on the pointer address of the Decimal class, or of some other object like that).

Crazy stuff.

Posted in techie notes. Tagged with , , , , , .

  • martinkl
    Ah, very glad to see that!
  • Robin
    In Python 3, this unexpected behaviour is fixed and there's a helpful error message:

    Python 3.0b3+ (py3k:66174, Sep 3 2008, 03:03:59)
    [GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> from decimal import Decimal
    >>> Decimal('1.0') < 2.0
    Traceback (most recent call last):
    File "", line 1, in
    TypeError: unorderable types: Decimal() < float()
blog comments powered by Disqus