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

facundo.batista python-checkins at python.org
Wed Sep 19 19:53:25 CEST 2007


Author: facundo.batista
Date: Wed Sep 19 19:53:25 2007
New Revision: 58211

Modified:
   python/trunk/Lib/decimal.py
   python/trunk/Lib/test/test_decimal.py
Log:

Issue #1772851.  Optimization of __hash__ to behave better for big big
numbers.


Modified: python/trunk/Lib/decimal.py
==============================================================================
--- python/trunk/Lib/decimal.py	(original)
+++ python/trunk/Lib/decimal.py	Wed Sep 19 19:53:25 2007
@@ -766,10 +766,17 @@
             if self._isnan():
                 raise TypeError('Cannot hash a NaN value.')
             return hash(str(self))
-        i = int(self)
-        if self == Decimal(i):
-            return hash(i)
-        assert self.__nonzero__()   # '-0' handled by integer case
+        if not self:
+            return 0
+        if self._isinteger():
+            op = _WorkRep(self.to_integral_value())
+            # to make computation feasible for Decimals with large
+            # exponent, we use the fact that hash(n) == hash(m) for
+            # any two nonzero integers n and m such that (i) n and m
+            # have the same sign, and (ii) n is congruent to m modulo
+            # 2**64-1.  So we can replace hash((-1)**s*c*10**e) with
+            # hash((-1)**s*c*pow(10, e, 2**64-1).
+            return hash((-1)**op.sign*op.int*pow(10, op.exp, 2**64-1))
         return hash(str(self.normalize()))
 
     def as_tuple(self):

Modified: python/trunk/Lib/test/test_decimal.py
==============================================================================
--- python/trunk/Lib/test/test_decimal.py	(original)
+++ python/trunk/Lib/test/test_decimal.py	Wed Sep 19 19:53:25 2007
@@ -910,6 +910,38 @@
     def test_hash_method(self):
         #just that it's hashable
         hash(Decimal(23))
+
+        test_values = [Decimal(sign*(2**m + n))
+                       for m in [0, 14, 15, 16, 17, 30, 31,
+                                 32, 33, 62, 63, 64, 65, 66]
+                       for n in range(-10, 10)
+                       for sign in [-1, 1]]
+        test_values.extend([
+                Decimal("-0"), # zeros
+                Decimal("0.00"),
+                Decimal("-0.000"),
+                Decimal("0E10"),
+                Decimal("-0E12"),
+                Decimal("10.0"), # negative exponent
+                Decimal("-23.00000"),
+                Decimal("1230E100"), # positive exponent
+                Decimal("-4.5678E50"),
+                # a value for which hash(n) != hash(n % (2**64-1))
+                # in Python pre-2.6
+                Decimal(2**64 + 2**32 - 1),
+                # selection of values which fail with the old (before
+                # version 2.6) long.__hash__
+                Decimal("1.634E100"),
+                Decimal("90.697E100"),
+                Decimal("188.83E100"),
+                Decimal("1652.9E100"),
+                Decimal("56531E100"),
+                ])
+
+        # check that hash(d) == hash(int(d)) for integral values
+        for value in test_values:
+            self.assertEqual(hash(value), hash(int(value)))
+
         #the same hash that to an int
         self.assertEqual(hash(Decimal(23)), hash(23))
         self.assertRaises(TypeError, hash, Decimal('NaN'))


More information about the Python-checkins mailing list