Почему размер 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 вы должны использовать новее, в котором также учитываются гораздо большие числа, поэтому он имеет еще несколько байтов.