机器数、真值、原码、反码和补码

795 阅读6分钟

机器数、真值、原码、反码和补码

文中所有二进制为了表示方便皆采用1字节来表示

一. 机器数、真值

1. 机器数

机器数就是一个数在计算机中的二进制表示,计算机中机器数的最高位是符号位,正数符号位为0,负数符号位为1。机器数包含原码、反码和补码三种表示形式,后文会具体进行解释。

例如:

十进制中的+2在计算机中的字长为8,转换成二进制为 0000 0010
十进制中的-2在计算机中的字长为8,转换成二进制为 1000 0010

上文例子中的0000 0010和1000 0010就是机器数

2. 机器数的真值

真值就是带符号位的机器数对应的真正数值,个人理解就是用正负号来代替符号位来表示机器数 例如:

机器数 0000 0010 的真值为 +000 0010 也就是+2
机器数 1000 0010 的真值为 -000 0010 也就是-2

二. 机器数的三种表示形式:原码、反码、补码

1. 原码

原码就是加了一位符号位的二进制数,正数符号位为0,负数符号位为1,符号位为最高位。 个人理解就是将真值里面的"+"转换为0,"-"转换为1。

十进制真值原码
+2+000 00100000 0010
-2-000 00101000 0010

由此可以得出8位的二进制数的大小范围为[1111 1111, 0111 1111],也就是[-127, 127]对应java中byte数据类型的大小范围

2. 反码

正数的反码就是其原码,负数的反码则是符号位不变,其他位取反(0变1,1变0)

十进制原码反码
+20000 00100000 0010
-21000 00101111 1101

3. 补码

正数的补码就是其原码,负数的补码则是反码+1

十进制原码反码补码
+20000 00100000 00100000 0010
-21000 00101111 11011111 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]。