Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Include/cpython/pyhash.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@

/* Parameters used for the numeric hash implementation. See notes for
_Py_HashDouble in Python/pyhash.c. Numeric hashes are based on
reduction modulo the prime 2**_PyHASH_BITS - 1. */
reduction modulo the prime 2**PyHASH_BITS - 1. */

#if SIZEOF_VOID_P >= 8
# define PyHASH_BITS 61
#else
# define PyHASH_BITS 31
#endif

#define PyHASH_MODULUS (((size_t)1 << _PyHASH_BITS) - 1)
#define PyHASH_MODULUS (((size_t)1 << PyHASH_BITS) - 1)
#define PyHASH_INF 314159
#define PyHASH_IMAG PyHASH_MULTIPLIER

Expand Down
6 changes: 3 additions & 3 deletions Modules/_decimal/_decimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -5799,15 +5799,15 @@ _decimal_Decimal___floor___impl(PyObject *self, PyTypeObject *cls)
static Py_hash_t
_dec_hash(PyDecObject *v)
{
#if defined(CONFIG_64) && _PyHASH_BITS == 61
#if defined(CONFIG_64) && PyHASH_BITS == 61
/* 2**61 - 1 */
mpd_uint_t p_data[1] = {2305843009213693951ULL};
mpd_t p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA, 0, 19, 1, 1, p_data};
/* Inverse of 10 modulo p */
mpd_uint_t inv10_p_data[1] = {2075258708292324556ULL};
mpd_t inv10_p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA,
0, 19, 1, 1, inv10_p_data};
#elif defined(CONFIG_32) && _PyHASH_BITS == 31
#elif defined(CONFIG_32) && PyHASH_BITS == 31
/* 2**31 - 1 */
mpd_uint_t p_data[2] = {147483647UL, 2};
mpd_t p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA, 0, 10, 2, 2, p_data};
Expand All @@ -5816,7 +5816,7 @@ _dec_hash(PyDecObject *v)
mpd_t inv10_p = {MPD_POS|MPD_STATIC|MPD_CONST_DATA,
0, 10, 2, 2, inv10_p_data};
#else
#error "No valid combination of CONFIG_64, CONFIG_32 and _PyHASH_BITS"
#error "No valid combination of CONFIG_64, CONFIG_32 and PyHASH_BITS"
#endif
const Py_hash_t py_hash_inf = 314159;
mpd_uint_t ten_data[1] = {10};
Expand Down
2 changes: 1 addition & 1 deletion Objects/complexobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ complex_hash(PyObject *op)
* compare equal must have the same hash value, so that
* hash(x + 0*j) must equal hash(x).
*/
combined = hashreal + _PyHASH_IMAG * hashimag;
combined = hashreal + PyHASH_IMAG * hashimag;
if (combined == (Py_uhash_t)-1)
combined = (Py_uhash_t)-2;
return (Py_hash_t)combined;
Expand Down
38 changes: 19 additions & 19 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3703,36 +3703,36 @@ long_hash(PyObject *obj)
#endif

while (--i >= 0) {
/* Here x is a quantity in the range [0, _PyHASH_MODULUS); we
/* Here x is a quantity in the range [0, PyHASH_MODULUS); we
want to compute x * 2**PyLong_SHIFT + v->long_value.ob_digit[i] modulo
_PyHASH_MODULUS.
PyHASH_MODULUS.

The computation of x * 2**PyLong_SHIFT % _PyHASH_MODULUS
The computation of x * 2**PyLong_SHIFT % PyHASH_MODULUS
amounts to a rotation of the bits of x. To see this, write

x * 2**PyLong_SHIFT = y * 2**_PyHASH_BITS + z
x * 2**PyLong_SHIFT = y * 2**PyHASH_BITS + z

where y = x >> (_PyHASH_BITS - PyLong_SHIFT) gives the top
where y = x >> (PyHASH_BITS - PyLong_SHIFT) gives the top
PyLong_SHIFT bits of x (those that are shifted out of the
original _PyHASH_BITS bits, and z = (x << PyLong_SHIFT) &
_PyHASH_MODULUS gives the bottom _PyHASH_BITS - PyLong_SHIFT
bits of x, shifted up. Then since 2**_PyHASH_BITS is
congruent to 1 modulo _PyHASH_MODULUS, y*2**_PyHASH_BITS is
congruent to y modulo _PyHASH_MODULUS. So
original PyHASH_BITS bits, and z = (x << PyLong_SHIFT) &
PyHASH_MODULUS gives the bottom PyHASH_BITS - PyLong_SHIFT
bits of x, shifted up. Then since 2**PyHASH_BITS is
congruent to 1 modulo PyHASH_MODULUS, y*2**PyHASH_BITS is
congruent to y modulo PyHASH_MODULUS. So

x * 2**PyLong_SHIFT = y + z (mod _PyHASH_MODULUS).
x * 2**PyLong_SHIFT = y + z (mod PyHASH_MODULUS).

The right-hand side is just the result of rotating the
_PyHASH_BITS bits of x left by PyLong_SHIFT places; since
not all _PyHASH_BITS bits of x are 1s, the same is true
after rotation, so 0 <= y+z < _PyHASH_MODULUS and y + z is
PyHASH_BITS bits of x left by PyLong_SHIFT places; since
not all PyHASH_BITS bits of x are 1s, the same is true
after rotation, so 0 <= y+z < PyHASH_MODULUS and y + z is
the reduction of x*2**PyLong_SHIFT modulo
_PyHASH_MODULUS. */
x = ((x << PyLong_SHIFT) & _PyHASH_MODULUS) |
(x >> (_PyHASH_BITS - PyLong_SHIFT));
PyHASH_MODULUS. */
x = ((x << PyLong_SHIFT) & PyHASH_MODULUS) |
(x >> (PyHASH_BITS - PyLong_SHIFT));
x += v->long_value.ob_digit[i];
if (x >= _PyHASH_MODULUS)
x -= _PyHASH_MODULUS;
if (x >= PyHASH_MODULUS)
x -= PyHASH_MODULUS;
}
x = x * sign;
if (x == (Py_uhash_t)-1)
Expand Down
26 changes: 13 additions & 13 deletions Python/pyhash.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ static Py_ssize_t hashstats[Py_HASH_STATS_MAX + 1] = {0};
#endif

/* For numeric types, the hash of a number x is based on the reduction
of x modulo the prime P = 2**_PyHASH_BITS - 1. It's designed so that
of x modulo the prime P = 2**PyHASH_BITS - 1. It's designed so that
hash(x) == hash(y) whenever x and y are numerically equal, even if
x and y have different types.

Expand All @@ -52,8 +52,8 @@ static Py_ssize_t hashstats[Py_HASH_STATS_MAX + 1] = {0};

If the result of the reduction is infinity (this is impossible for
integers, floats and Decimals) then use the predefined hash value
_PyHASH_INF for x >= 0, or -_PyHASH_INF for x < 0, instead.
_PyHASH_INF and -_PyHASH_INF are also used for the
PyHASH_INF for x >= 0, or -PyHASH_INF for x < 0, instead.
PyHASH_INF and -PyHASH_INF are also used for the
hashes of float and Decimal infinities.

NaNs hash with a pointer hash. Having distinct hash values prevents
Expand All @@ -65,16 +65,16 @@ static Py_ssize_t hashstats[Py_HASH_STATS_MAX + 1] = {0};
efficiently, even if the exponent of the binary or decimal number
is large. The key point is that

reduce(x * y) == reduce(x) * reduce(y) (modulo _PyHASH_MODULUS)
reduce(x * y) == reduce(x) * reduce(y) (modulo PyHASH_MODULUS)

provided that {reduce(x), reduce(y)} != {0, infinity}. The reduction of a
binary or decimal float is never infinity, since the denominator is a power
of 2 (for binary) or a divisor of a power of 10 (for decimal). So we have,
for nonnegative x,

reduce(x * 2**e) == reduce(x) * reduce(2**e) % _PyHASH_MODULUS
reduce(x * 2**e) == reduce(x) * reduce(2**e) % PyHASH_MODULUS

reduce(x * 10**e) == reduce(x) * reduce(10**e) % _PyHASH_MODULUS
reduce(x * 10**e) == reduce(x) * reduce(10**e) % PyHASH_MODULUS

and reduce(10**e) can be computed efficiently by the usual modular
exponentiation algorithm. For reduce(2**e) it's even better: since
Expand All @@ -92,7 +92,7 @@ _Py_HashDouble(PyObject *inst, double v)

if (!isfinite(v)) {
if (isinf(v))
return v > 0 ? _PyHASH_INF : -_PyHASH_INF;
return v > 0 ? PyHASH_INF : -PyHASH_INF;
else
return PyObject_GenericHash(inst);
}
Expand All @@ -109,19 +109,19 @@ _Py_HashDouble(PyObject *inst, double v)
and hexadecimal floating point. */
x = 0;
while (m) {
x = ((x << 28) & _PyHASH_MODULUS) | x >> (_PyHASH_BITS - 28);
x = ((x << 28) & PyHASH_MODULUS) | x >> (PyHASH_BITS - 28);
m *= 268435456.0; /* 2**28 */
e -= 28;
y = (Py_uhash_t)m; /* pull out integer part */
m -= y;
x += y;
if (x >= _PyHASH_MODULUS)
x -= _PyHASH_MODULUS;
if (x >= PyHASH_MODULUS)
x -= PyHASH_MODULUS;
}

/* adjust for the exponent; first reduce it modulo _PyHASH_BITS */
e = e >= 0 ? e % _PyHASH_BITS : _PyHASH_BITS-1-((-1-e) % _PyHASH_BITS);
x = ((x << e) & _PyHASH_MODULUS) | x >> (_PyHASH_BITS - e);
/* adjust for the exponent; first reduce it modulo PyHASH_BITS */
e = e >= 0 ? e % PyHASH_BITS : PyHASH_BITS-1-((-1-e) % PyHASH_BITS);
x = ((x << e) & PyHASH_MODULUS) | x >> (PyHASH_BITS - e);

x = x * sign;
if (x == (Py_uhash_t)-1)
Expand Down
6 changes: 3 additions & 3 deletions Python/sysmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1587,10 +1587,10 @@ get_hash_info(PyThreadState *tstate)
} while(0)

SET_HASH_INFO_ITEM(PyLong_FromLong(8 * sizeof(Py_hash_t)));
SET_HASH_INFO_ITEM(PyLong_FromSsize_t(_PyHASH_MODULUS));
SET_HASH_INFO_ITEM(PyLong_FromLong(_PyHASH_INF));
SET_HASH_INFO_ITEM(PyLong_FromSsize_t(PyHASH_MODULUS));
SET_HASH_INFO_ITEM(PyLong_FromLong(PyHASH_INF));
SET_HASH_INFO_ITEM(PyLong_FromLong(0)); // This is no longer used
SET_HASH_INFO_ITEM(PyLong_FromLong(_PyHASH_IMAG));
SET_HASH_INFO_ITEM(PyLong_FromLong(PyHASH_IMAG));
SET_HASH_INFO_ITEM(PyUnicode_FromString(hashfunc->name));
SET_HASH_INFO_ITEM(PyLong_FromLong(hashfunc->hash_bits));
SET_HASH_INFO_ITEM(PyLong_FromLong(hashfunc->seed_bits));
Expand Down
Loading