diff options
author | Facundo Batista <facundobatista@gmail.com> | 2007-09-19 17:53:25 (GMT) |
---|---|---|
committer | Facundo Batista <facundobatista@gmail.com> | 2007-09-19 17:53:25 (GMT) |
commit | 8c202440699cef19602acc24822366d0d7c32083 (patch) | |
tree | c8dbd3260c57ba194db7aed972a3709da66b6e2c /Lib | |
parent | ae406c60180fbaa6d30b82ec2174ea38f9ba746f (diff) | |
download | cpython-8c202440699cef19602acc24822366d0d7c32083.zip cpython-8c202440699cef19602acc24822366d0d7c32083.tar.gz cpython-8c202440699cef19602acc24822366d0d7c32083.tar.bz2 |
Issue #1772851. Optimization of __hash__ to behave better for big big
numbers.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/decimal.py | 15 | ||||
-rw-r--r-- | Lib/test/test_decimal.py | 32 |
2 files changed, 43 insertions, 4 deletions
diff --git a/Lib/decimal.py b/Lib/decimal.py index 4137d06..6ca8bab 100644 --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -766,10 +766,17 @@ class Decimal(object): 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): diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 9281abc..1647212 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -910,6 +910,38 @@ class DecimalUsabilityTest(unittest.TestCase): 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')) |