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


241

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

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

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


9
Не уверен, но я думаю, что это так же намеренно, как и hash(float('nan'))быть 0.
cs95

1
Хм, никаких упоминаний об этом в sys.hash_info. Пасхальное яйцо?
Вим

123
Спросите Тима Питерса. Вот коммит, где он представил эту константу 19 лет назад: github.com/python/cpython/commit/… . Я сохранил эти специальные значения, когда переработал числовой хеш в bugs.python.org/issue8188
Марк Дикинсон

8
@MarkDickinson Спасибо. Похоже, что Тим изначально использовал цифры e для хэша -inf.
Вим

17
@ wim Ах да, правда. И, видимо, я изменил это на -314159. Я забыл об этом.
Марк Дикинсон

Ответы:


47

_PyHASH_INFбудет определена как константа , равными 314159.

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


6
Небольшая мелочь: по определению почти неизбежно, что то же значение будет использоваться для других хэшей, например, в этом случае hash(314159)также 314159. Также попробуйте в Python 3 hash(2305843009214008110) == 314159(это входные данные 314159 + sys.hash_info.modulus) и т. Д.
ShreevatsaR

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

220

Резюме: это не совпадение; _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атрибутом из встроенного типа поплавка ( 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)in 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 .

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


44
Выбор -271828 для -Inf устраняет любые сомнения, что ассоциация пи была случайной.
Рассел

24
@RussellBorogove Нет, но это делает его примерно в миллион раз менее вероятным;)
труба

8
@cmaster: Смотрите часть выше , где он говорит , май 2010, а именно раздел документации по хеширования числовых типов и выпуска 8188 - идея заключается в том , что мы хотим hash(42.0)быть такими же , как hash(42), и так же , как hash(Decimal(42))и hash(complex(42))и hash(Fraction(42, 1)). Решение (от Марка Дикинсона) является элегантным IMO: определение математической функции, которая работает для любого рационального числа, и использование факта, что числа с плавающей точкой также являются рациональными числами.
ShreevatsaR

1
@ ShreevatsaR Ах, спасибо. Хотя я не хотел бы гарантировать такое равенство, полезно знать, что есть хорошее,
надежное

2
@cmaster Хеш-функция для целых чисел просто hash(n) = n % Mгде M = (2 ^ 61 - 1). Это обобщается для рационального n к hash(p/q) = (p/q) mod Mс делением, интерпретируемым по модулю M (другими словами:) hash(p/q) = (p * inverse(q, M)) % M. Причина, по которой мы хотим этого: если dмы поместим в диктовку, d[x] = fooа затем получим x==y(например, 42.0 == 42), но d[y]это не то же самое d[x], то у нас возникнет проблема. Большая часть, казалось бы, сложного кода происходит от природы самого формата с плавающей запятой для правильного восстановления дроби и необходимости специальных случаев для значений inf и NaN.
ShreevatsaR

12

На самом деле,

sys.hash_info.inf

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

hash(float('-inf'))

возвращает -271828, или примерно -e, в Python 2 (сейчас это -314159 ).

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.