Почему размер 2⁶³ 36 байт, но 2⁶³ -1 составляет всего 24 байта?
Все в Python - это объект. Таким образом, размер int в Python будет больше обычного.
>>> sys.getsizeof(int())
24
Хорошо, но почему требуется еще 2 байта для 2⁶³
по сравнению с 2⁶³ - 1
а не только один?
>>> sys.getsizeof(2**63)
36
>>> sys.getsizeof(2**62)
24
Я получаю, что 2⁶³
- это длинный и 2⁶³-1
int, но почему 12 байтов разницы?
Не более интуитивно, я пробовал некоторые другие вещи:
>>> a = 2**63
>>> a -= 2**62
>>> sys.getsizeof(a)
36
a
по-прежнему сохраняется как длинный, даже если он может быть в int сейчас. Так что неудивительно. Но:
>>> a -= (2**63 - 1)
>>> a = 2**63
>>> a -= (2**63 - 1)
>>> a
1L
>>> sys.getsizeof(a)
28
Новый размер.
>>> a = 2**63
>>> a -= 2**63
>>> a
0L
>>> sys.getsizeof(a)
24
Возврат к 24 байтам, но с длинным.
Последнее, что я получил:
>>> sys.getsizeof(long())
24
Вопрос:
Как память хранится в этих сценариях?
Суб-вопросы:
Почему существует пробел в 12 байт, чтобы добавить то, что говорит нам наша интуиция, всего лишь 1 бит?
Почему int()
и long()
24 байта, но long(1)
уже 28 байтов и int(2⁶²)
?
NB: Python 3.X работает немного по-другому, но не более интуитивно. Здесь я сосредоточился на Python 2.7; Я не тестировал предыдущие версии.
Ответы
Ответ 1
почему он получает еще 12 байтов для 2⁶³ по сравнению с 2⁶³ - 1, а не только один?
В системе LP64 1 Python int
состоит из фиксированных трех частей размера указателя:
- указатель типа
- счетчик ссылок
- фактическое значение, C
long int
Всего 24 байта. С другой стороны, Python long
состоит из:
- указатель типа
- счетчик ссылок
- число цифр, целое число указателя
- встроенный массив цифр значений, каждый из которых содержит 30 бит значения, но сохраняется в 32-битных единицах (один из неиспользуемых битов используется для эффективного переноса/записи во время сложения и вычитания)
2 ** 63 требует хранения 64 бит, поэтому он помещается в три 30-битные цифры. Поскольку каждая цифра имеет ширину 4 байта, весь Python long
будет принимать 24 + 3 * 4 = 36 байт.
Другими словами, разница возникает из-за long
хранения размера номера (8 дополнительных байтов), и от него немного меньше пространства для хранения значения (12 байтов для хранения цифр 2 ** 63). В том числе размер, значение 2 ** 63 в long
занимает 20 байтов. Сравнивая это с 8 байтами, занятыми любым значением простого int
получаем наблюдаемую 12-байтовую разницу.
1 64-разрядная версия Windows отличается тем, что сохраняет 32-битный long int
, предположительно для совместимости с исходным кодом, с большим количеством старого кода, который использовал char
, short
и long
как "удобные" псевдонимы для 8, 16 и 32-битных значений, которые работали как на 16, так и на 32-битных системах. Чтобы получить фактический 64-разрядный тип на Windows x86-64, нужно использовать __int64
или (в более новых версиях компилятора) long long
или int64_t
. Поскольку Python 2.7 внутренне зависит от того, как Python int
вписывается в C в разных местах, sys.maxint
остается 2**31-1
, даже в 64-битной Windows. Все это исправлено в Python 3, чей единственный тип int
имеет переменную ширину, например Python 2 long
.
Ответ 2
Хотя я не нашел его в документации, вот мое объяснение.
Python 2 поддерживает int
до long
неявно, когда значение превышает значение, которое может быть сохранено в int. Размер нового типа (long
) - это размер по умолчанию long
, который равен 32. С этого момента размер вашей переменной будет определяться его значением, которое может увеличиваться и уменьшаться.
from sys import getsizeof as size
a = 1
n = 32
# going up
for i in range(10):
if not i:
print 'a = %100s%13s%4s' % (str(a), type(a), size(a))
else:
print 'a = %100s%14s%3s' % (str(a), type(a), size(a))
a <<= n
# going down
for i in range(11):
print 'a = %100s%14s%3s' % (str(a), type(a), size(a))
a >>= n
a = 1 <type 'int'> 24
a = 4294967296 <type 'long'> 32
a = 18446744073709551616 <type 'long'> 36
a = 79228162514264337593543950336 <type 'long'> 40
a = 340282366920938463463374607431768211456 <type 'long'> 44
a = 1461501637330902918203684832716283019655932542976 <type 'long'> 48
a = 6277101735386680763835789423207666416102355444464034512896 <type 'long'> 52
a = 26959946667150639794667015087019630673637144422540572481103610249216 <type 'long'> 56
a = 115792089237316195423570985008687907853269984665640564039457584007913129639936 <type 'long'> 60
a = 497323236409786642155382248146820840100456150797347717440463976893159497012533375533056 <type 'long'> 64
a = 2135987035920910082395021706169552114602704522356652769947041607822219725780640550022962086936576 <type 'long'> 68
a = 497323236409786642155382248146820840100456150797347717440463976893159497012533375533056 <type 'long'> 64
a = 115792089237316195423570985008687907853269984665640564039457584007913129639936 <type 'long'> 60
a = 26959946667150639794667015087019630673637144422540572481103610249216 <type 'long'> 56
a = 6277101735386680763835789423207666416102355444464034512896 <type 'long'> 52
a = 1461501637330902918203684832716283019655932542976 <type 'long'> 48
a = 340282366920938463463374607431768211456 <type 'long'> 44
a = 79228162514264337593543950336 <type 'long'> 40
a = 18446744073709551616 <type 'long'> 36
a = 4294967296 <type 'long'> 32
a = 1 <type 'long'> 28
Как вы можете видеть, тип остается long
после того, как он стал слишком большим для int
, а начальный размер был 32, но размер изменяется со значением (может быть выше или ниже [или равно, очевидно] до 32)
Итак, чтобы ответить на ваш вопрос, размер базы составляет 24 для int
, а 28 для long
, а в то же long
имеет место для сохранения больших значений (которое начинается как 4 байта - следовательно, 32 байта в long
, но может идти вверх и вниз в соответствии с к значению)
Что касается вашего подзапроса, создание уникального типа (с уникальным размером) для нового номера невозможно, поэтому у Python есть "подклассы" long
типа, которые имеют дело с диапазоном чисел, поэтому, если вы превысите лимит из вашего старого long
вы должны использовать новее, в котором также учитываются гораздо большие числа, поэтому он имеет еще несколько байтов.