个人博客:The Blog Of WaiterXiaoYY 欢迎来互相交流学习。
虽然之前学过《计算机组成原理》,也理解了原码、补码、反码等东西,但终究还是理解不够深刻,
最近在做位运算类型的题目的时候,脑子突然闪过到一个问题,
为什么 int 型数据类型的取值范围不对称呢?
为什么最大值是 2^31 - 1
呢?
为什么最小值是 -2^31
,而不是-2^31 + 1
呢?
正如标题中所说,很惭愧,直到今天才真正弄明白,
但亡羊补牢,终究未晚。
我们知道,计算机能够认识的只有二进制(也就是 0 和 1),而我们所认识的字符和数字都要转换成二进制才能让计算机识别并执行。
这里以java的int型为例说明,
我们首先要明白的是,
java的 int 型是32位的,
因为一个 int 值占 4 个字节 byte ,一个字节是 8 位 bit(即8个二进制位),所以 int型 占 32 位,
其中第32位,也就是最高位是符号位,正数为 0,负数为 1,
剩下的31位是用来表示数字部分。
正数在计算机中表示为 原码,
比如:
1 的原码是 :0000 0000 0000 0000 0000 0000 0000 0001
1 的补码是:0000 0000 0000 0000 0000 0000 0000 0001
1 的反码是:0000 0000 0000 0000 0000 0000 0000 0001
没错,正数的 原码 、补码 、 反码 都相同,
那么最大是多少呢?
当然是除了符号位,其他位置上都为 1 的时候,
0111 1111 1111 1111 1111 1111 1111 1111
这个数就是 2147483647,它是 32 位中所能表示的最大正数。
负数在计算机中表示为 补码,
比如:
-1 的原码是:1000 0000 0000 0000 0000 0000 0000 0001
-1 的反码是:1111 1111 1111 1111 1111 1111 1111 1110
-1 的补码是:1111 1111 1111 1111 1111 1111 1111 1111
很明显,负数的 原码 、补码 、 反码 并不相同,
而且,
负数的原码 是在 正数的原码 上 将符号位取反 取反,
负数的反码 是在 负数的原码 上 除符号位后 取反,
负数的补码 是在 负数的反码 上 加一。
负数的补码 也可以说是在 负数的原码 上 取反加一。
所以我们再来看看 -2147483647 的表示,
-2147483647 的原码是:1111 1111 1111 1111 1111 1111 1111 1111
-2147483647 的反码是:1000 0000 0000 0000 0000 0000 0000 0000
-2147483647 的补码是:1000 0000 0000 0000 0000 0000 0000 0001
那它是最小值吗?
不是,还有一个很奇怪的东西。
在二进制中,0 有两种表示方法,
+0 的原码:0000 0000 0000 0000 0000 0000 0000 0000
-0 的原码:1000 0000 0000 0000 0000 0000 0000 0000
因为 0 只需要一个,所以就把 -0 当成了最小的数 -2147483648
可以这么理解,正因为 0 有两种表示方式,所以会多了一个负数出来,
-2147483648 的补码就是:1000 0000 0000 0000 0000 0000 0000 0000
,它在 32位里面是没有原码的。
但需要注意的是,这个补码并不是真正的补码,
真正的补码应该是 1100 0000 0000 0000 0000 0000 0000 0000
,但在 java 中溢出了,
所以,就是1000 0000 0000 0000 0000 0000 0000 0000
经过上面,还可搞懂为什么8位的范围是[-128, 127],是不是有点绕?
嗯,我也觉得,但总算有点恍然大悟的样子。
整理于 2020.4.13