[Python-Dev] Decimal <-> float comparisons in py3k.

Mark Dickinson dickinsm at gmail.com
Tue Mar 16 15:41:26 CET 2010


Hello all,

Currently in Python 2.x, Decimal-to-float comparisons behave as follows:

>>> Decimal(1) < float(4)
False
>>> Decimal(4) < float(1)
False

That is, any Decimal sorts before any float (though this is arbitrary:
 it's possible that on some implementations any float sorts before any
Decimal).  This causes (a) confusion, and (b) bugs, especially when
floats and Decimals are accidentally combined in a program.  There
probably aren't too many legitimate reasons for deliberately mixing
floats and Decimals in the same calculation, so preventing accidents
is the main concern here.

In Python 3.x, however, such comparisons raise a TypeError
("unorderable types: Decimal() < float()").

http://bugs.python.org/issue2531 ('float compared to decimal is
silently incorrect') was opened for this a while ago.

I'm planning to commit a change to trunk that changes the behaviour so
that the comparison result is based on the respective values of the
arguments (so the results above would be True and False respectively).

Question for python-dev people (and the point of this email): should
this change be forward ported to py3k?

On the one hand there's something to be said for maintaining a clean
separation between the float and Decimal types, allowing only explicit
conversions from one to the other;  mixed-type arithmetic between
floats and Decimals was very deliberately not permitted in the
original PEP, and that's unlikely to change in a hurry.  On the other
hand, there's value in keeping 2.x and 3.x aligned where possible for
the sake of 2-to-3 porters, and the new behaviour may even be useful.
Even with the TypeError above, there are still some py3k surprises
arising from the ability to compare ints and Decimals, and ints and
floats, but not floats and Decimals.

A quick tour of some of these surprises, in trunk:

>>> from decimal import Decimal
>>> Decimal(1) < 2 < float(3) < Decimal(1)  # < is non-transitive
True
>>> Decimal(1) == 1 == float(1)   # so is equality
True
>>> Decimal(1) == float(1)
False
>>> d1, i1, f1 = Decimal(1), float(1), 1
>>> set([d1, i1, f1]) == set([f1, i1, d1])  # sets with the same elements are different
False
>>> sorted([d1, i1, f1]) == sorted([f1, i1, d1])
False

and in py3k:

>>> from decimal import Decimal
>>> Decimal(1) < 2 < float(3) < Decimal(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: float() < Decimal()
>>> Decimal(1) == 1 == float(1)
True
>>> Decimal(1) == float(1)
False
>>> d1, i1, f1 = Decimal(1), float(1), 1
>>> set([d1, i1, f1]) == set([f1, i1, d1])
False
>>> sorted([Decimal(1), 2, float(3)])
[Decimal('1'), 2, 3.0]
>>> sorted([2, Decimal(1), float(3)])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: float() < Decimal()
>>> sorted([float(3), 2, Decimal(1)])
[Decimal('1'), 2, 3.0]

By the way, even with the patch there are still problems with other
numeric types: comparisons or set operations involving both Fraction
and Decimal instances are going to cause similar difficulties to those
above.  In practice I think this is much less of an issue than the
float/Decimal problem, since the chance of accidentally combining
Fraction and Decimal types in a calculation seems significantly
smaller than the chance of accidentally combining float and Decimal
types.

--
Mark


More information about the Python-Dev mailing list