在以前的 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://segmentfault.com/a/1190000015284473
Python 3 的 int 类型详解(为什么 int 不存在溢出问题?)
来源: http://www.bubuko.com/infodetail-3280587.html