Хэш бесконечности в Python имеет цифры, соответствующие пи:
>>> inf = float('inf')
>>> hash(inf)
314159
>>> int(math.pi*1e5)
314159
Это просто совпадение или это намеренно?
Хэш бесконечности в Python имеет цифры, соответствующие пи:
>>> inf = float('inf')
>>> hash(inf)
314159
>>> int(math.pi*1e5)
314159
Это просто совпадение или это намеренно?
Резюме: это не совпадение; _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.
Связанные позже коммиты:
Марк Дикинсон в апреле 2010 года (также), заставляя тип Decimal
вести себя аналогично
Марк Дикинсон (Mark Dickinson) в апреле 2010 года (также), переместив эту проверку в начало и добавив контрольные примеры
Марк Дикинсон (Mark Dickinson) в мае 2010 года под номером 8188 полностью переписал хеш-функцию в своей текущей реализации, но сохранил этот особый случай, присвоив константе имя _PyHASH_INF
(также удаляя 271828, поэтому в hash(float('-inf'))
Python 3 hash(float('-inf'))
возвращает -314159
а не -271828
как в Python 2)
Рэймонд Хеттингер (Raymond Hettinger) в январе 2011 года добавил явный пример в "Что нового" для Python 3.2 из sys.hash_info
показывающий указанное выше значение. (Смотрите здесь.)
Стефан Крах в марте 2012 года изменил модуль Decimal, но сохранил этот хэш.
Кристиан Хаймс в ноябре 2013 года переместил определение _PyHASH_INF
из Include/pyport.h
в Include/pyhash.h
где он сейчас находится.
_PyHASH_INF
определяется как константа, равная 314159
.
Я не могу найти никаких обсуждений по этому поводу или комментариев, приводящих причину. Я думаю, что это было выбрано более или менее произвольно. Я полагаю, что если они не используют одно и то же значимое значение для других хэшей, это не должно иметь значения.
В самом деле,
sys.hash_info.inf
возвращает 314159
. Значение не генерируется, оно встроено в исходный код. По факту,
hash(float('-inf'))
возвращает -271828
или приблизительно -e в Python 2 (теперь это -314159).
Тот факт, что два самых известных иррациональных числа всех времен используются в качестве значений хеш-функции, делает маловероятным совпадение.