机器数和真值
1、机器数
一个数在计算机中的二进制表示形式就是这个数的机器数。机器数是带符号的,在计算机用机器数的最高位存放符号,正数为0,负数为1。
比如,十进制中的数 +5 ,计算机字长为8位,转换成二进制就是0000 0101。如果是 -5,就是 1000 0101 。
那么,这里的 0000 0101 和 1000 0101 就是机器数。
2、机器数的真值
因为第一位是符号位,所以机器数的形式值就不等于真正的数值。
例如上面的符号数 1000 0101,其最高位1代表负,其真正数值是 -5,而不是形式值131(1000 0011转换成十进制等于133)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。
比如:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = -000 0001 = -1
二. 机器数的三种表示形式:原码、反码、补码
1. 原码
原码就是加了一位符号位的二进制数,正数符号位为0,负数符号位为1,符号位为最高位。 个人理解就是将真值里面的"+"转换为0,"-"转换为1。
| 十进制 | 真值 | 原码 | | :---: | :---: | :---: | | +2 | +000 0010 | 0000 0010 | | -2 | -000 0010 | 1000 0010 |
由此可以得出8位的二进制数的大小范围为[1111 1111, 0111 1111],也就是[-127, 127]对应java中byte数据类型的大小范围
2. 反码
正数的反码就是其原码,负数的反码则是符号位不变,其他位取反(0变1,1变0)
| 十进制 | 原码 | 反码 | | :---: | :---: | :---: | | +2 | 0000 0010 | 0000 0010 | | -2 | 1000 0010 | 1111 1101 |
3. 补码
正数的补码就是其原码,负数的补码则是反码+1
| 十进制 | 原码 | 反码 | 补码 | | :---: | :---: | :---: | :---: | | +2 | 0000 0010 | 0000 0010 | 0000 0010 | | -2 | 1000 0010 | 1111 1101 | 1111 1110 |
4. 为何使用反码、补码
使用原码进行计算的时候,对于人而言能够很轻易的辨别出符号位,然后直接对其他位数值进行计算。然而对于计算机的设计而言辨别出符号位就是一项非常复杂的工程,所以设计的时候就考虑让符号位直接参与计算,这样设计计算机就十分简单了。 对于加法而言符号位对于计算并没有影响,对于减法而言则考虑通过加上负数来转换为加法的方式进行计算。 如果通过原码来直接进行减法计算:
3 - 2
= 3 + (-2)
= 0000 0011(原) + 1000 0010(原)
= 1000 0101
= -5
结果显而易见,如果通过原码来直接让符号位参与运算的话是不正确的,所以为了解决减法的问题引入了反码的概念。如果通过反码来进行减法计算:
3 - 2
= 3 + (-2)
= 0000 0011(原) + 1000 0010(原)
= 0000 0011(反) + 1111 1101(反)
= 1 0000 0000(反) -- 最高位产生进位,结果+1
= 0000 0001(反)
= 0000 0001(原)
= 1
结果正确,从上面例子看来如果通过反码进行减法运算的话是没有问题的,那为什么又需要补码呢,我们一起来看下面这个特殊的例子:
2 - 2
= 2 + (-2)
= 0000 0010(原) + 1000 0010(原)
= 0000 0010(反) + 1111 1101(反)
= 1111 1111(反)
= 1000 0000(原)
= -0
0 + 0
= 0000 0000(原) + 0000 0000(原)
= 0000 0000(反) + 0000 0000(反)
= 0000 0000(反)
= 0000 0000(原)
= 0
由于对于0这个数字而言,正负号没有任何意义,但是经过计算却有可能出现[0000 0000]和[1000 0000]这两种不同的原码表示同一个数字0,这显然是不合理的,所以此时就引入了补码的概念。 如果通过补码来进行上述例子的计算:
2 - 2
= 2 + (-2)
= 0000 0010(原) + 1000 0010(原)
= 0000 0010(反) + 1111 1101(反)
= 0000 0010(补) + 1111 1110(补)
= 1 0000 0000(补) -- 最高位产生进位,进位舍弃
= 0000 0000(补)
= 0000 0000(反)
= 0000 0000(原)
= 0
0 + 0
= 0000 0000(原) + 0000 0000(原)
= 0000 0000(反) + 0000 0000(反)
= 0000 0000(补) + 0000 0000(补)
= 0000 0000(反)
= 0000 0000(原)
= 0
由上述例子可以看出,补码完美的解决了0的符号问题以及0有两个不同原码表示的问题。而且[10000 0000]也可以用来表示-128:
-1 - 127
= -1 + (-127)
= 1000 0001(原) + 1111 1111(原)
= 1111 1110(反) + 1000 0000(反)
= 1111 1111(补) + 1000 0001(补)
= 1 1000 0000(补) --最高位产生进位,进位舍弃
= 1000 0000(补)
-1 - 127的结果为-128,上面例子中-1和-127补码相加后得出的补码也是-128。但是这个1000 0000(补)实际上对应的是之前的-0,所以这个补码是没有反码和原码的。 综上可以看出使用补码的话不仅0的符号问题和多原码问题可以解决,还可以多表示一个最小数。因此对于1字节而言,原码和反码的范围是[-127, 127],而补码的范围是[-128, 127],也可以解释java中int的范围是[-2, 2-1]。