]> granicus.if.org Git - python/commitdiff
Issue #1757: The hash of a Decimal instance is no longer affected
authorFacundo Batista <facundobatista@gmail.com>
Tue, 8 Jan 2008 12:25:20 +0000 (12:25 +0000)
committerFacundo Batista <facundobatista@gmail.com>
Tue, 8 Jan 2008 12:25:20 +0000 (12:25 +0000)
by the current context.  Thanks Mark Dickinson.

Lib/decimal.py
Lib/test/test_decimal.py
Misc/NEWS

index 257ba0c118b152a5ba8cd2b7d3e101d8830d255c..3ee078f570daf1303521f28fe776a42a380df86a 100644 (file)
@@ -788,8 +788,10 @@ class Decimal(object):
     def __hash__(self):
         """x.__hash__() <==> hash(x)"""
         # Decimal integers must hash the same as the ints
-        # Non-integer decimals are normalized and hashed as strings
-        # Normalization assures that hash(100E-1) == hash(10)
+        #
+        # 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:
             if self._isnan():
                 raise TypeError('Cannot hash a NaN value.')
@@ -805,7 +807,13 @@ class Decimal(object):
             # 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()))
+        # The value of a nonzero nonspecial Decimal instance is
+        # faithfully represented by the triple consisting of its sign,
+        # its adjusted exponent, and its coefficient with trailing
+        # zeros removed.
+        return hash((self._sign,
+                     self._exp+len(self._int),
+                     self._int.rstrip('0')))
 
     def as_tuple(self):
         """Represents the number as a triple tuple.
index dbe70231867e18b1a34318a8be77a6c4f150c28a..03cff604bb0a1b3e2d0211b0a909879e418a6d29 100644 (file)
@@ -980,6 +980,23 @@ class DecimalUsabilityTest(unittest.TestCase):
         self.assert_(hash(Decimal('Inf')))
         self.assert_(hash(Decimal('-Inf')))
 
+        # check that the value of the hash doesn't depend on the
+        # current context (issue #1757)
+        c = getcontext()
+        old_precision = c.prec
+        x = Decimal("123456789.1")
+
+        c.prec = 6
+        h1 = hash(x)
+        c.prec = 10
+        h2 = hash(x)
+        c.prec = 16
+        h3 = hash(x)
+
+        self.assertEqual(h1, h2)
+        self.assertEqual(h1, h3)
+        c.prec = old_precision
+
     def test_min_and_max_methods(self):
 
         d1 = Decimal('15.32')
index 4699e1bf91480b010ab2426c59ebdb02cd8a5208..0a0153400a8cac42ac9f1494da8be2f4d3739e0e 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -348,6 +348,9 @@ Core and builtins
 Library
 -------
 
+- Issue #1757: The hash of a Decimal instance is no longer affected by
+  the current context.
+
 - Patch #467924: add ZipFile.extract() and ZipFile.extractall() in the
   zipfile module.