web-dev-qa-db-fra.com

Pourquoi le hachage de Python à l'infini a-t-il les chiffres de π?

Le hachage de l'infini dans Python a des chiffres correspondant pi :

>>> inf = float('inf')
>>> hash(inf)
314159
>>> int(math.pi*1e5)
314159

Est-ce juste une coïncidence ou est-ce intentionnel?

240
wim

_PyHASH_INF est défini comme une constante égal à 314159.

Je ne trouve aucune discussion à ce sujet, ni aucun commentaire donnant une raison. Je pense qu'il a été choisi plus ou moins arbitrairement. J'imagine que tant qu'ils n'utilisent pas la même valeur significative pour d'autres hachages, cela ne devrait pas avoir d'importance.

46
Patrick Haugh

Résumé: Ce n'est pas une coïncidence; _PyHASH_INF Est codé en dur comme 314159 dans l'implémentation CPython par défaut de Python, et a été choisi comme valeur arbitraire (évidemment à partir des chiffres de π) par Tim Peters en 20 .


La valeur de hash(float('inf')) est l'un des paramètres dépendants du système de la fonction de hachage intégrée pour les types numériques, et est également disponible comme sys.hash_info.inf Dans Python 3:

>>> import sys
>>> sys.hash_info
sys.hash_info(width=64, modulus=2305843009213693951, inf=314159, nan=0, imag=1000003, algorithm='siphash24', hash_bits=64, seed_bits=128, cutoff=0)
>>> sys.hash_info.inf
314159

(Même résultats avec PyPy aussi.)


En termes de code, hash est une fonction intégrée. L'appeler sur un objet float Python appelle la fonction dont le pointeur est donné par l'attribut tp_hash du type float intégré (PyTypeObject PyFloat_Type ), qui est la fonction float_hash, définie comme return _Py_HashDouble(v->ob_fval), qui à son tour a

    if (Py_IS_INFINITY(v))
        return v > 0 ? _PyHASH_INF : -_PyHASH_INF;

_PyHASH_INF est défini comme 314159:

#define _PyHASH_INF 314159

En termes d'histoire, la première mention de 314159 Dans ce contexte dans le code Python (vous pouvez le trouver avec git bisect Ou git log -S 314159 -p) A été ajoutée par Tim Peters en août 2000, dans ce qui est maintenant commit 9dce29 dans le référentiel cpython git.

Le message de validation dit:

Correction pour http://sourceforge.net/bugs/?func=detailbug&bug_id=111866&group_id=547 . C'était un bug trompeur - le vrai "bug" était que hash(x) renvoyait une erreur lorsque x est un infini. Correction de ça. Ajout d'une nouvelle macro Py_IS_INFINITY À pyport.h. Code réarrangé pour réduire la duplication croissante dans le hachage du flottant et des nombres complexes, poussant Trent plus tôt à cela à une conclusion logique. Correction d'un bug extrêmement rare où le hachage des flottants pouvait retourner -1 même s'il n'y avait pas d'erreur (n'a pas perdu de temps à essayer de construire un cas de test, il était tout simplement évident d'après le code qu'il pourrait se produire). Hachage complexe amélioré afin que hash(complex(x, y)) ne soit plus systématiquement égal à hash(complex(y, x)).

En particulier, dans ce commit, il a extrait le code de static long float_hash(PyFloatObject *v) dans Objects/floatobject.c Et l'a fait simplement return _Py_HashDouble(v->ob_fval);, et dans la définition de long _Py_HashDouble(double v) dans Objects/object.c, il a ajouté les lignes:

        if (Py_IS_INFINITY(intpart))
            /* can't convert to long int -- arbitrary */
            v = v < 0 ? -271828.0 : 314159.0;

Donc, comme mentionné, c'était un choix arbitraire. Notez que 271828 est formé à partir des premiers chiffres décimaux de e .

Commits ultérieurs associés:

219
ShreevatsaR

En effet,

sys.hash_info.inf

retour 314159. La valeur n'est pas générée, elle est intégrée au code source. En réalité,

hash(float('-inf'))

retour -271828, ou approximativement -e, dans python 2 ( c'est -314159 maintenant ).

Le fait que les deux nombres irrationnels les plus célèbres de tous les temps soient utilisés comme valeurs de hachage rend très peu probable une coïncidence.

12
Alec Alameddine