Хеш нескінченності в Python має цифри, відповідні pi :
>>> inf = float('inf')
>>> hash(inf)
314159
>>> int(math.pi*1e5)
314159
Це просто збіг чи це навмисне?
sys.hash_info
. Пасхальне яйце?
-314159
. Я забув про це.
Хеш нескінченності в Python має цифри, відповідні pi :
>>> inf = float('inf')
>>> hash(inf)
314159
>>> int(math.pi*1e5)
314159
Це просто збіг чи це навмисне?
sys.hash_info
. Пасхальне яйце?
-314159
. Я забув про це.
Відповіді:
_PyHASH_INF
буде визначено як константа , яка дорівнює314159
.
Я не можу знайти жодної дискусії з цього приводу або коментарів, що дають причину. Я думаю, що його обирали більш-менш довільно. Я гадаю, що поки вони не використовують однакове значення для інших хешів, це не має значення.
hash(314159)
також 314159
. Також спробуйте, в Python 3, hash(2305843009214008110) == 314159
(цей вхід є 314159 + sys.hash_info.modulus
) тощо
Резюме: Це не випадковість; у впровадженні CPython Python за замовчуванням _PyHASH_INF
жорстко кодується як 314159 , і Тим Петерс у 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
. Впорядкований код для зменшення дублювання у хешировании float і комплексних чисел, підштовхуючи попередній удар Trent до логічного завершення. Виправлена надзвичайно рідкісна помилка, при якій хеширование плавців може повернутися до -1, навіть якщо не було помилки (не витрачаючи часу на конструювання тестового випадку, з коду було просто очевидно, що це може статися). Удосконалено складний хеш, щобhash(complex(x, y))
систематичноhash(complex(y, x))
більше не дорівнювати .
Зокрема, у цьому документі він видобув код 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 утворюється з перших кількох десяткових цифр е .
Пов’язані пізніші зобов’язання:
За Марком Дікінсоном у квітні 2010 року ( також ), змушуючи Decimal
тип вести себе аналогічно
Автор Марка Дікінсона у квітні 2010 року ( також ), перемістивши цей чек на початок і додавши тестові випадки
Марк Дікінсон в травні 2010 року як випуск 8188 , повністю переписав хеш-функцію до її поточної реалізації , але зберігаючи цей особливий випадок, даючи постійне ім'я _PyHASH_INF
(також видаляючи 271828, тому в Python 3 hash(float('-inf'))
повертається, -314159
а не -271828
як у Python 2)
Автор Реймонд Хеттінгер у січні 2011 року , додавши явний приклад у "Що нового" для Python 3.2 sys.hash_info
показує вищевказане значення. (Дивіться тут .)
Штефан Крах у березні 2012 року модифікував модуль Decimal, але зберігав цей хеш.
Крістіан Хеймс у листопаді 2013 року переніс визначення _PyHASH_INF
з того Include/pyport.h
місця, Include/pyhash.h
де воно живе зараз.
hash(42.0)
бути такими ж hash(42)
, також такими ж, як hash(Decimal(42))
і hash(complex(42))
і hash(Fraction(42, 1))
. Рішення (за Марком Дікінсоном) - це елегантний ІМО: визначення математичної функції, яка працює для будь-якого раціонального числа, та використання факту, що числа з плаваючою комою є і раціональними числами.
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.
Дійсно,
sys.hash_info.inf
повертає 314159
. Значення не генерується, воно вбудовано у вихідний код. Фактично,
hash(float('-inf'))
повертається -271828
, або приблизно -e, в python 2 ( зараз -314159 ).
Той факт, що два найвідоміші ірраціональні числа усіх часів використовуються як хешові значення, робить його малоймовірним випадковістю.
hash(float('nan'))
буття0
.