[Python-checkins] r79589 - in python/trunk: Lib/decimal.py Lib/test/test_decimal.py Misc/NEWS

mark.dickinson python-checkins at python.org
Fri Apr 2 12:35:12 CEST 2010


Author: mark.dickinson
Date: Fri Apr  2 12:35:12 2010
New Revision: 79589

Log:
Issue #7279:  Make Decimal('nan') hashable.  Decimal('snan') remains unhashable.

Also rewrite the Decimal __hash__ method so that it doesn't rely on
float('inf') being valid: float('inf') could raise an exception on
platforms not using IEEE 754 arithmetic.


Modified:
   python/trunk/Lib/decimal.py
   python/trunk/Lib/test/test_decimal.py
   python/trunk/Misc/NEWS

Modified: python/trunk/Lib/decimal.py
==============================================================================
--- python/trunk/Lib/decimal.py	(original)
+++ python/trunk/Lib/decimal.py	Fri Apr  2 12:35:12 2010
@@ -935,14 +935,30 @@
         # The hash of a nonspecial noninteger Decimal must depend only
         # on the value of that Decimal, and not on its representation.
         # For example: hash(Decimal('100E-1')) == hash(Decimal('10')).
-        if self._is_special and self._isnan():
-            raise TypeError('Cannot hash a NaN value.')
+
+        # Equality comparisons involving signaling nans can raise an
+        # exception; since equality checks are implicitly and
+        # unpredictably used when checking set and dict membership, we
+        # prevent signaling nans from being used as set elements or
+        # dict keys by making __hash__ raise an exception.
+        if self._is_special:
+            if self.is_snan():
+                raise TypeError('Cannot hash a signaling NaN value.')
+            elif self.is_nan():
+                # 0 to match hash(float('nan'))
+                return 0
+            else:
+                # values chosen to match hash(float('inf')) and
+                # hash(float('-inf')).
+                if self._sign:
+                    return -271828
+                else:
+                    return 314159
 
         # In Python 2.7, we're allowing comparisons (but not
         # arithmetic operations) between floats and Decimals;  so if
         # a Decimal instance is exactly representable as a float then
-        # its hash should match that of the float.  Note that this takes care
-        # of zeros and infinities, as well as small integers.
+        # its hash should match that of the float.
         self_as_float = float(self)
         if Decimal.from_float(self_as_float) == self:
             return hash(self_as_float)

Modified: python/trunk/Lib/test/test_decimal.py
==============================================================================
--- python/trunk/Lib/test/test_decimal.py	(original)
+++ python/trunk/Lib/test/test_decimal.py	Fri Apr  2 12:35:12 2010
@@ -1274,6 +1274,10 @@
     def test_hash_method(self):
         #just that it's hashable
         hash(Decimal(23))
+        hash(Decimal('Infinity'))
+        hash(Decimal('-Infinity'))
+        hash(Decimal('nan123'))
+        hash(Decimal('-NaN'))
 
         test_values = [Decimal(sign*(2**m + n))
                        for m in [0, 14, 15, 16, 17, 30, 31,
@@ -1308,7 +1312,7 @@
 
         #the same hash that to an int
         self.assertEqual(hash(Decimal(23)), hash(23))
-        self.assertRaises(TypeError, hash, Decimal('NaN'))
+        self.assertRaises(TypeError, hash, Decimal('sNaN'))
         self.assertTrue(hash(Decimal('Inf')))
         self.assertTrue(hash(Decimal('-Inf')))
 

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Fri Apr  2 12:35:12 2010
@@ -37,7 +37,8 @@
 
 - Issue #7279: Comparisons involving a Decimal signaling NaN now
   signal InvalidOperation instead of returning False.  (Comparisons
-  involving a quiet NaN are unchanged.)
+  involving a quiet NaN are unchanged.)  Also, Decimal quiet NaNs
+  are now hashable;  Decimal signaling NaNs remain unhashable.
 
 - Issue #2531: Comparison operations between floats and Decimal
   instances now return a result based on the numeric values of the


More information about the Python-checkins mailing list