classification
Title: Make Decimal comparisons with NaN less arbitrary
Type: behavior Stage:
Components: Library (Lib) Versions: Python 3.0
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: facundobatista Nosy List: christian.heimes, facundobatista, mark.dickinson, rhettinger
Priority: normal Keywords: patch

Created on 2008-01-31 04:46 by mark.dickinson, last changed 2008-02-07 16:08 by mark.dickinson. This issue is now closed.

Files
File name Uploaded Description Edit
richcmp.patch mark.dickinson, 2008-01-31 04:46
richcmp_signal.patch mark.dickinson, 2008-01-31 17:43
Messages (8)
msg61884 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2008-01-31 04:46
For Python 3.0 the decimal module got rich comparisons.  Those comparisons have somewhat unconventional 
behavior with respect to NaNs, as seen below:  <, <= and == comparisons involving NaNs always return False, 
while >, >= and != comparisons always return True.

The Decimal specification has nothing helpful to say about comparisons involving NaNs.  But reading IEEE-
754r (on which the Decimal specification is closely based), there are two possible options:

 (1) have comparisons involving NaNs (except for !=) raise InvalidOperation in the context, and hence give a 
Python exception (assuming that InvalidOperation isn't trapped.)

 (2) have comparisons involving NaNs always return False (except for !=, which always returns True).

I think either of these is better than the current behavior.  (2) seems like the better option, for a couple 
of reasons:  first, it's the way that Python floats currently work, and second, there might be issues with 
list membership testing if equality comparisons involving NaNs raised an exception or returned a NaN.

Since Mike Cowlishaw is intimately involved with both the Decimal specification and the IEEE-754r process, I 
thought it might be useful to have his opinion on this;  his main recommendation was to have the Decimal 
type do the same as the float type.

The attached patch makes <, <=, >, >= and == comparisons involving NaNs always return False, and != 
comparisons always return True.  It also renames __cmp__ to _cmp and adds a few tests for the new behavior.

Here's how NaN comparisons currently work:

Python 3.0a2+ (py3k:60470M, Jan 30 2008, 23:11:40) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from decimal import *
>>> n = Decimal("nan")
>>> i = Decimal("inf")
>>> n < n
False
>>> n > n
True
>>> i < n
False
>>> i > n
True

See also issue #1514428.
msg61885 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2008-01-31 05:22
ISTM, you're now moving far beyond the spec into territory that isn't 
covered by tests or standards.  Is this necessary?
msg61894 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2008-01-31 10:06
Yes, I think it's necessary.

Speaking of standards, it's the current behavior which isn't backed by any 
standard or rationale other than the historical one involving now-defunct 3-
way comparisons.

The proposed behavior is much closer to that specified by C99 (see sections 
7.12.14 and section F.3) and by IEEE 754 and its upcoming revision (see 
section 5.11 of the most recent draft, which is linked to from the wikipedia 
page for IEEE 754r).  Even better from a standards-compliance perspective 
would be to have all comparisons except != involving NaNs raise the 
InvalidOperation flag.

The current behavior also breaks something fundamental:  namely, that x < y 
should have the same truth value as y > x.  Python even depends on this for 
reversing comparison operators, which explains, but in my opinion doesn't 
excuse, the following oddity:

>>> 2 < Decimal("NaN")
True
>>> Decimal(2) < Decimal("NaN")
False
msg61914 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2008-01-31 17:43
Here's a patch (richcmp_signal.patch) that gives an alternative way of doing things:  all 
<, <=, > and >= comparisons involving a NaN raise the InvalidOperation flag (using exactly 
the same semantics as those specified for compare_signal). == and != comparisons always 
return False and True, respectively, just as they do now.

So the post-patch decimal exactly follows IEEE 754(r) on this.  I think it makes good sense 
from a user's viewpoint, too:  with the default settings, any <, <=, > or >= comparison 
involving a NaN raises a Python exception; this seems like the best outcome for a beginning 
Decimal user, while expert users can choose to use the public compare or compare_signal 
methods instead.

My worries about list membership testing were nonsensical:  there's only one reasonable 
behaviour for == and !=, namely the current behavior, which also matches all the standards.

On balance, I think I prefer this approach to the idea of returning False on comparisons 
involving NaNs.  The only fly in the ointment is that floats and Decimals behave 
differently in this respect.
msg62110 - (view) Author: Facundo Batista (facundobatista) * (Python committer) Date: 2008-02-06 18:04
I'm +0 regarding this.

If this will go in, a comment should explicit all this in the code, as
this behaviour doesn't come from Decimal spec or the PEP.

Thanks!
msg62126 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2008-02-06 22:13
Okay:  I checked in this change in r60630.  The checkin includes comments 
in the code and an extra paragraph describing this behavior in the 
documentation.
msg62141 - (view) Author: Facundo Batista (facundobatista) * (Python committer) Date: 2008-02-07 11:39
Thanks Mark!

Shall this issue be closed?
msg62155 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2008-02-07 16:08
Closing.
History
Date User Action Args
2008-02-07 16:08:14mark.dickinsonsetstatus: open -> closed
resolution: fixed
messages: + msg62155
2008-02-07 11:39:46facundobatistasetmessages: + msg62141
2008-02-06 22:13:30mark.dickinsonsetmessages: + msg62126
2008-02-06 18:04:11facundobatistasetmessages: + msg62110
2008-01-31 17:43:26mark.dickinsonsetfiles: + richcmp_signal.patch
messages: + msg61914
2008-01-31 13:34:08christian.heimessetpriority: normal
nosy: + christian.heimes
2008-01-31 10:06:07mark.dickinsonsetmessages: + msg61894
2008-01-31 05:22:38rhettingersetnosy: + rhettinger
messages: + msg61885
2008-01-31 04:46:16mark.dickinsoncreate