Почему Python хэш бесконечности имеет цифры π? - программирование

Почему Python хэш бесконечности имеет цифры π?

Хэш бесконечности в Python имеет цифры, соответствующие пи:

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

Это просто совпадение или это намеренно?

4b9b3361

Ответ 1

Резюме: это не совпадение; _PyHASH_INF жестко закодирован как 314159 в реализации Python по умолчанию на CPython и был выбран Тимом Петерсом в 2000 году как произвольное значение (очевидно, из цифр π).


Значение hash(float('inf')) является одним из системных параметров встроенной хэш-функции для числовых типов, а также доступно как sys.hash_info.inf в 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

(Те же результаты с PyPy тоже.)


С точки зрения кода, hash является встроенной функцией. Вызов его для объекта с плавающей точкой Python вызывает функцию, указатель которой tp_hash атрибутом tp_hash встроенного типа с плавающей точкой (PyTypeObject PyFloat_Type), который является функцией float_hash, определенной как return _Py_HashDouble(v->ob_fval), которая в очередь имеет

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

где _PyHASH_INF определяется как 314159:

#define _PyHASH_INF 314159

С точки зрения истории, первое упоминание о 314159 в этом контексте в коде Python (вы можете найти это с помощью git bisect или git log -S 314159 -p) было добавлено Тимом Питерсом в августе 2000 года, в том, что сейчас является коммитом. 39dce293 в репозитории cpython git.

Сообщение коммита говорит:

Исправление для http://sourceforge.net/bugs/?func=detailbug&bug_id=111866&group_id=5470. Это ошибочная ошибка - настоящая "ошибка" заключалась в том, что hash(x) возвращал ошибку, когда x - бесконечность. Исправлено Добавлен новый Py_IS_INFINITY макрос pyport.h. Переставил код, чтобы уменьшить растущее дублирование в хешировании чисел с плавающей запятой и комплексных чисел, что подтолкнуло Трента к этому логическому завершению. Исправлена чрезвычайно редкая ошибка, из-за которой хэширование чисел с плавающей точкой могло возвращать -1, даже если ошибки не было (не тратя время на попытки создания тестового примера, из кода было просто очевидно, что это может произойти). Улучшен сложный хеш, так что hash(complex(x, y)) систематически не равен hash(complex(y, x)).

В частности, в этом static long float_hash(PyFloatObject *v) он вырвал код static long float_hash(PyFloatObject *v) в Objects/floatobject.c и заставил его просто return _Py_HashDouble(v->ob_fval); и в определении long _Py_HashDouble(double v) в Objects/object.c он добавил строки:

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

Как уже упоминалось, это был произвольный выбор. Обратите внимание, что 271828 формируется из первых нескольких десятичных цифр e.

Связанные позже коммиты:

Ответ 2

_PyHASH_INF определяется как константа, равная 314159.

Я не могу найти никаких обсуждений по этому поводу или комментариев, приводящих причину. Я думаю, что это было выбрано более или менее произвольно. Я полагаю, что если они не используют одно и то же значимое значение для других хэшей, это не должно иметь значения.

Ответ 3

В самом деле,

sys.hash_info.inf

возвращает 314159. Значение не генерируется, оно встроено в исходный код. По факту,

hash(float('-inf'))

возвращает -271828 или приблизительно -e в Python 2 (теперь это -314159).

Тот факт, что два самых известных иррациональных числа всех времен используются в качестве значений хеш-функции, делает маловероятным совпадение.