После проверки переменной в 'most-positive-fixnum' в emacs выводится:
most-positive-fixnum is a variable defined in ‘data.c’.
Its value is 2305843009213693951
Documentation:
The largest value that is representable in a Lisp integer.
Это значение log2:
In [8]: math.log2(2305843009213693951)
Out[8]: 61.0
почему установлено как 2 **61
, а не 2**62
или 2**63
?
Потому что есть биты тегов: при встрече с объектом язык с динамической типизацией должен знать, какого он типа. Для этого должна быть некоторая информация о «теге», которая говорит об этом. Наивным подходом было бы представление всех объектов как минимум двумя машинными словами: словом информации тега и хотя бы одним словом для самого объекта. Это катастрофа для таких объектов, как целые числа, поскольку это означает, что вам нужно два машинных слова даже для небольших целых чисел, которые вы ожидаете уместить в одно. Это, в свою очередь, означает, что, например, (+ 1 2)
необходимо выделить хранилище, чего вы бы не хотели.
Чтобы обойти это, вы уменьшаете максимальный размер небольших целых чисел (фиксумов) и используете некоторые запасные биты в слове в качестве тега, который говорит «это фиксированное число». Существует очень хитрый трюк, который вы можете сделать здесь, называемый «младшими тегами»: если вы зарезервируете, например, два бита тега в нижнем конце слова, вы можете сделать их такими (в порядке битов с прямым порядком байтов):
Умная вещь заключается в том, что это означает, что ваши fixnums могут быть на один бит больше, чем вы ожидаете, потому что младший бит четного fixnum равен 0: он перекрывается с битами тега. Сложение с фиксированными числами, представленными таким образом, может использовать машинные операции (переполнение по модулю), в то время как умножение требует сдвига, который обычно практически бесплатен, поскольку не затрагивает память. Я не знаю, использует ли elisp низкие теги, хотя я предполагаю, что да.