Skip to content


Comparing floating-point and decimal in Python

Published by Martin Kleppmann on 02 Sep 2008.

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.