[Python-Dev] Decimal <-> float comparisons in py3k. (original) (raw)

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 "", line 1, in 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 "", line 1, in 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