第一个问题:我们都知道一个二进制8位能表示的最大值是 1111 1111 == 255,但为什么最大表示到127?
因为对于计算机来说,一个二进制的数字它的最高位是符号位,0表示正数,1表示负数。
所以 1111 1111 表示的 -127, 而 0111 1111 表示的是127,范围区间应该是[-127,127]之间。那么第二个问题来了
第二个问题:我们都知道一个Byte能表达的数字范围是[-128,127],那么这个-128是怎么来的呢?
这里面就涉及到计算机的原码、反码、和补码的相关知识了。
正数:
原码 == 反码 == 补码
即:原:0000 0001 ----- 反: 0000 0001 ------ 补:0000 0001
负数:
反码 == 原码的非符号位取反
补码 == 反码+1
即 原: 1000 0001 ------ 反: 1111 1110 ------ 补: 1111 1111
原码、反码、补码都可以表示同一个数字
计算机内部是用补码来存储一个数的
为什么要用补码来存呢?
既然规定了最高位是符号位,那么对于一个人来说正常的加减一个数(比如: 0010 1101 + 0111 1100, 0001 1011 - 1011 0011)可以把这个二进制转成带符号的十进制自然进行加减。
而对于计算机来说,同样需要识别符号位来判断是要加还是要减,也就这样势必会加大计算机的复杂性。因此希望符号位也可以参与到运算中,也就是 100 - 50 == 100 + (-50)。把减法也可以转化成加法。
这里举一个最简单的例子:
1-1 == 1+(-1) == 0
那如果是两个原码相加是什么样的
0000 0001
+
1000 0001
=
1000 0010 == -2 结果肯定是不正确的
那如果两个反码相加
0000 0001
+
1111 1110
=
1111 1111 转成原码 就是 1000 0000 == -0 负0
结果没有问题,但从数学的角度来说,一个整数包括负整数,0,正整数,而这里出现了负0,-0和0其实都表示0,但是这里从编码来说却出现了两个值, 1000 0000 和 0000 0000
最后看下两个补码相加
0000 0001
+
1111 1111
=
0000 0000 (超过8位的值被截掉) 转成原码 还是 0000 0000 =0,
可以看出,结果不仅正确,还同时解决了正负0的问题。
而对于补码的加运算来说,是不可能出现1000 0000的情况,因为两数相加想出现1000 0000的情况,就必然是两个正数相加(如 0000 0001 + 0111 1111 = 1000 0000),而两个正数相加是肯定不会出现负数的,所以1000 0000这个数肯定不会相加出现。
因此计算机规定,补码:1000 0000 就表示 -128
所以8位二进制数可表达的范围是[-2^7, 2^7-1]=[-128,127]
所以总结一下,补码不仅能正确的运算,同时还可以多表示一个最低位,所以计算机用补码来存储数字。
同理,16位,32位等等的其他二进制位的表示范围也就是[-2^15, 2^15-1], [-2^31, 2^31-1]