在以前的Python2中,整型分為int和long,也就是整型和長整型, 長整型不存在溢出問題, 即可以存放任意大小的數值,理論支持無限大數字。
因此在Python3 中,統一使用長整型,用int表示,在Python3中不存在long,只有int。
這個長整形int結構其實也很簡單, 在 longintepr.h 中定義:
struct _longobject {
PyObject_VAR_HEAD
digit ob_digit[1];
};
ob_digit 它是一個數組指針。digit 可認為是 int 的別名.
即長整型在Python內部是用一個int數組(digit ob_digit[n])保存值的. 待存儲的數值的低位信息放於低位下標, 高位信息放於高下標.比如要保存 112233445566778899 很長,但我們的int只能保存6位(假設):
那么python就會這樣存儲:
ob_digit[0] = 778899;
ob_digit[1] = 445566;
ob_digit[2] = 112233;
低位存於低索引下,高位位於高索引下。而正負符號信息由ob_size保存, 像上面的例子中對象元素個數是3, 那么ob_size = 3 而如果表示數是負數的, 那么 ob_size = -3
python中整型結構中的數組,每個元素最大存儲 15 位的二進制數(不同位數操作系統有差異32位系統存15位,64位系統是30位)。
如64位系統最大存儲30位的二進制數,即存儲的最大十進制數是 2^30-1 = 1073741823,也就是說上面例子中數組一個元素存儲的最大值是1073741823。
那么存儲數字 10737418231 其實是:
ob_digit[0] = 1;
ob_digit[1] = 1073741823;
需要注意的是,實際存儲是以二進制形式存儲,而非我們所寫的十進制。
十進制:1073741823 = 二進制:11111...11111(30位) 存儲在高索引 1
十進制:1 = 二進制:00000...000001(30位) 存儲在低索引 0
1 ~ 2^30-1 需要一個數組元素存放
2^30 ~ 2^60-1 需要兩個數組元素存放
以此類推……
通過代碼來看:
import sys
print("2^30 = {}\n2^60 = {}".format(1024*1024*1024, 1024*1024*1024*1024*1024*1024))
print("0, 1, 2^30-1, 2^30, 2^60-1 的字節大小: ",sys.getsizeof(0), sys.getsizeof(1), sys.getsizeof(1073741823), sys.getsizeof(1073741824), sys.getsizeof(1152921504606846975))
結果如下:
2^30 = 1073741824
2^60 = 1152921504606846976
數字 0, 1, 2^30-1, 2^30, 2^60-1,2^60 的字節大小: 24 28 28 32 32 36
由於Python中的int有一個基礎內存占用(也就是長整形結構中PyObject_VAR_HEAD占用內存的大小,24字節),因此數字 1 ~ 2^30-1 內存大小是28字節,2^30 ~ 2^60-1 內存大小是32字節,這里需要注意的是 0 占用的內存大小是24字節而非28字節!
一個數組元素的所用內存大小是4字節即32位,但其實存儲數字的有效位是30位(64位系統中),少的兩位去哪了???
實際存儲只用了30位的原因是:指數運算中要求位移量得是 5 的倍數,可能是某種優化算法。
見源碼:
https://github.com/python/cpython/blob/d8c93aa5d29d3cab537357018d5806a57452a8fe/Include/longintrepr.h#L24
https://github.com/python/cpython/blob/d8c93aa5d29d3cab537357018d5806a57452a8fe/Objects/longobject.c#L4449