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.
This commit is contained in:
parent
e096e82e82
commit
f3eeca16cb
|
@ -935,14 +935,30 @@ class Decimal(object):
|
||||||
# The hash of a nonspecial noninteger Decimal must depend only
|
# The hash of a nonspecial noninteger Decimal must depend only
|
||||||
# on the value of that Decimal, and not on its representation.
|
# on the value of that Decimal, and not on its representation.
|
||||||
# For example: hash(Decimal('100E-1')) == hash(Decimal('10')).
|
# 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
|
# In Python 2.7, we're allowing comparisons (but not
|
||||||
# arithmetic operations) between floats and Decimals; so if
|
# arithmetic operations) between floats and Decimals; so if
|
||||||
# a Decimal instance is exactly representable as a float then
|
# a Decimal instance is exactly representable as a float then
|
||||||
# its hash should match that of the float. Note that this takes care
|
# its hash should match that of the float.
|
||||||
# of zeros and infinities, as well as small integers.
|
|
||||||
self_as_float = float(self)
|
self_as_float = float(self)
|
||||||
if Decimal.from_float(self_as_float) == self:
|
if Decimal.from_float(self_as_float) == self:
|
||||||
return hash(self_as_float)
|
return hash(self_as_float)
|
||||||
|
|
|
@ -1274,6 +1274,10 @@ class DecimalUsabilityTest(unittest.TestCase):
|
||||||
def test_hash_method(self):
|
def test_hash_method(self):
|
||||||
#just that it's hashable
|
#just that it's hashable
|
||||||
hash(Decimal(23))
|
hash(Decimal(23))
|
||||||
|
hash(Decimal('Infinity'))
|
||||||
|
hash(Decimal('-Infinity'))
|
||||||
|
hash(Decimal('nan123'))
|
||||||
|
hash(Decimal('-NaN'))
|
||||||
|
|
||||||
test_values = [Decimal(sign*(2**m + n))
|
test_values = [Decimal(sign*(2**m + n))
|
||||||
for m in [0, 14, 15, 16, 17, 30, 31,
|
for m in [0, 14, 15, 16, 17, 30, 31,
|
||||||
|
@ -1308,7 +1312,7 @@ class DecimalUsabilityTest(unittest.TestCase):
|
||||||
|
|
||||||
#the same hash that to an int
|
#the same hash that to an int
|
||||||
self.assertEqual(hash(Decimal(23)), hash(23))
|
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')))
|
||||||
self.assertTrue(hash(Decimal('-Inf')))
|
self.assertTrue(hash(Decimal('-Inf')))
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,8 @@ Library
|
||||||
|
|
||||||
- Issue #7279: Comparisons involving a Decimal signaling NaN now
|
- Issue #7279: Comparisons involving a Decimal signaling NaN now
|
||||||
signal InvalidOperation instead of returning False. (Comparisons
|
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
|
- Issue #2531: Comparison operations between floats and Decimal
|
||||||
instances now return a result based on the numeric values of the
|
instances now return a result based on the numeric values of the
|
||||||
|
|
Loading…
Reference in New Issue