简单的说,计算机就是晶体管、电路板组装起来对的电子设备,无论是图形图像的渲染、网络远程共享,还是大数据计算,归根结底都是 0 与 1 的信号处理。信息存储和逻辑计算的元数据,只能是 0 与 1,但是它们在不同介质里的物理表现方式却是不一样的,如三极管的断电与通电、CPU的低电平与高电平、磁盘的电荷左右方向。明确了0与1的物理表现方式后,设定基数为2,进位规则是“逢二进一”,借位规则是“借一当二”,所以称为二进制。

回归正题,如何计算二进制数的取值范围呢?比如,Java中的 int 数据类型是32位,那么 int 所能表现的范围是多少呢?

设想有 8 条电路,每条电路有低电平和高电平两种状态。根据数学排列组合,有 8 个 2 相乘,即 2^8,能够表示 256 种不同的信号。假设表示区间为 0~255,最大数即为 2^8 -1 = 256 -1 = 255,减去 1 的原因是因为第一个数用来表示 0,所以可表示的总数减 1 就等于最大数,那么 32 条电路能够表示的最大数为 (2^32-1)= 4,294,967,295。平时所说的 32 位机器,就能够同时处理字长为 32 位的电路信号。

如何表示负数呢?上面的 8 条电路,最左侧的一条表示正负,0 表示正数,1 表示负数,该位为符号位,不参与数值运算表示。8 条电路,实际的数值部分只有 7 位,那么最大值用二进制表示即为 0111 1111 即 127。

你也可以换种思维去理解,数值部分有 7 位,根据数学排列组合,有 7 个 2 相乘,即 2^7 ,能够表示 128 种信号。加上第一位符号位的 0 或 1,能够表示 128 x 2 = 256 种信号。包括正数 128 个、负数 128 个,范围即 0 ~ 127、-128 ~ -1,表示范围因有正负之分而改变为 -128 ~127。

二进制整数最终都是以补码形式出现的。正数的补码与原码、反码是一样的,而负数的补码是反码加 1 的结果。这样使减法运算可以使用加法器实现,符号位也参与运算。需要注意的是,负数的原码、反码、补码的符号位始终是 1,只有数值部分参与原码到反码、原码到补码的转换运算。

回到原来的问题,Java中的 int 数据类型是 32 位,那么 int 所能表现的范围是多少呢?

数值部分所能表示的范围个数是 2^31,算上符号位的 0 或 1,即正数范围 0 ~ 2^31-1,负数范围为 -2^31 ~ -1,合计为 -2^31 ~ 2^31-1。


参考资料

《码出高效:Java开发手册》