文章目录
原码、反码和补码的解释及设计原因
一、前言
在计算机中存储的所有数字都是二进制数据,当电压大于3.3伏的时候计算机认为该数据为1,当电压小于3.3伏时,计算机认为该数据为0。但是在计算机中存储我们日常使用的十进制数据的时候不是直接存储的该数字的二进制表示形式,而是存储的该数字对应二进制的补码。所以接下来就来聊一聊计算机中的原码、反码和补码。
1、机器数和真值
在了解原码、反码和补码之前,先介绍机器数和真值
机器数: 一个数在计算机的存储形式是二进制数,我们称这些二进制数为机器数。在计算机存储中机器数是有符号的,在机器数的最高位存放的是改数字的符号,0表示正数,1表示负数。
真值: 由于在计算机中存放的数据是有符号位的,(用一byte举例)机器数为:1000 0011 在计算机中该数字的真值为 -3 但是当打开计算器输入这个二进制数字后发现,其值为:131。因此将带符号的机器数的真正表示的值称为机器数的真值。
二、原码、反码和补码
1、原码
原码:十进制数据的二进制表示形式,最左边为符号位,0为正,1为负。
首先声明一个 byte 有 8bit ,之后的例子中都是以 1 byte作为例子。
例如:
5 -> 0000 0101
2 -> 0000 0010
1 -> 0000 0001
当女神看到的 521 变成这个样子你接下来的表情可能是这样的:
所以在计算机中一个byte的最大值: 0111 1111 (+127)最小值:1111 1111 (-127)
但是在这里原码有个两个弊端:
0000 0000 -> +0
1000 0000 -> -0
第一个就是在原码中有两个0存在
第二个弊端结合一个数轴来理解
1000 0000表示的就是数轴的原点:
把上边的数字进行 +1之后我们预想的结果应该是:
但是在原码中 +1 之后为1000 0001,也就是真值-1,在数轴上是:
总结:
弊端一:原码中有两个0存在
弊端二:在负数运算中,运算结果与我们的预期是相反的(在正数中是正确的)
所以为了解决第二个弊端,一帮计算机大佬们就提出了:在负数的时候,可以将数轴也反过来这样上边的两条小狗就可以跑向同一个方向了。这样就出现了反码。
2、反码
原码:正数的反码为其本身,负数的反码是符号位保持不变,其余位取反。
十进制数字 | 原码 | 反码 |
---|---|---|
+1 | 0000 0001 | 0000 0001 |
+0 | 0000 0000 | 0000 0000 |
-0 | 1000 0000 | 1111 1111 |
-1 | 1000 0001 | 1111 1110 |
-2 | 1000 0010 | 1111 1101 |
-3 | 1000 0011 | 1111 1100 |
-4 | 1000 0100 | 1111 1011 |
-5 | 1000 0101 | 1111 1010 |
为什么正数的反码是不变的呢?
上一节说到原码的负数在进行计算的时候才会出现真实值(计算结果)和期望值的两个狗狗出现向反方向跑的情况,在正数中不存在这样的情况,所以正数是的反码是不变的。
这个时候我们试一下反码的计算是否正确呢
2.1、负数计算
测试:-56 - 1
-56原码为 1011 1000 反码为:1100 0111 。该反码 -1 之后的结果为 1100 0110
-57原码为 1011 1001 反码为:1100 0110
然后将这个反码与我们的期望值 -57 的反码进行比较,明显发现相同。
从上边反码的表格中可以发现,只在反码范围内加减都没有任何问题。
这个时候是负数不跨 0 的运算
由于在反码中有两个 0 的存在,所以当进行跨 0 运算的时候就能发现问题。
2.2 、反码跨 0 的运算
在这张图中是 反码中 -5+6 的过程,通过计算结果可以发现,在反码表中 -5+6的结果为 +0 这是计算的结果,但是预期结果应该为 +1 出现该问题就是因为在反码表中出现了两个 0
同样的在**-4+7**的计算过程中也存在相同的问题,
所以在这种情况下计算机大佬们又来解决问题了。方法就是将所有的负数都向后推一位。这样就将反码中两个 0 的情况屏蔽掉了,具体结果就是下一节的补码。
3、补码
原码:正数的补码使其本身,负数的补码是在其反码的基础上+1
具体的结果就是:
十进制数字 | 原码 | 反码 | 补码 |
---|---|---|---|
+1 | 0000 0001 | 0000 0001 | 0000 0001 |
+0 | 0000 0000 | 0000 0000 | 0000 0000 |
-0 | 1000 0000 | 1111 1111 | 0000 0000 |
-1 | 1000 0001 | 1111 1110 | 1111 1111 |
-2 | 1000 0010 | 1111 1101 | 1111 1110 |
-3 | 1000 0011 | 1111 1100 | 1111 1101 |
-4 | 1000 0100 | 1111 1011 | 1111 1100 |
-5 | 1000 0101 | 1111 1010 | 1111 1011 |
… | … | … | … |
-127 | 1111 1111 | 1000 0000 | 1000 0001 |
-128 | 无 | 无 | 1000 0000 |
其最终结果就是向后一位
3.1、不跨 0 运算
-56 + 1
-56原码为 1011 1000 ,反码为:1100 0111,补码为:1100 1000
结果
1 1 0 0 1 0 0 0 (-56)
0 0 0 0 0 0 0 1 ( 1)
--------------------------
1 1 0 0 1 0 0 1
-55原码为 1011 0111 反码为:1100 1000,补码为:1100 1001
然后将这个补码与我们的期望值 -55 的补码进行比较,明显发现相同。
3.2、跨 0 运算
计算 -3 +5 的结果
上表可以看出 -3 的补码为:1111 1101
运算
1 1 1 1 1 1 0 1 (-3)
0 0 0 0 0 1 0 1 ( 5)
--------------------------
0 0 0 0 0 0 1 0
我猜你现在的表情是不是这个
4、总结
至此原码、反码和补码的部分就全部讲解完毕了。当然在计算机中所有的数字都是以补码的形式存在的,补码很好的解决了原码中存在的两个问题:
- 在负数运算中,运算结果与我们的预期是相反的
- 在原码中有两个0存在
由于解决了两个 0 的问题所以在补码中多出来一位,在计算机的数字中这多出来的一位记录一个特殊的值 -128 ,这也是在原码和补码中没有的。
三、为何要是用原码、反码和补码
知道了什么是原码、反码和补码,那么大家肯定有一个疑问:对于我们人类来说,原码才是可以直接并进行运算的,那么计算机中为什么不直接记录原码,而是这么麻烦的记录大家并不熟悉的补码呢?
在大家人类的认知中,看到一个数就可以很轻松的知道这个数是正数还是复数(通过最左边的符号位),但是大家有没有想过对于一台计算机,一个电路板来说判断第一个是一个符号位是一件很困难的事情。
大家都知道在运算过程中减去一个数,等于加这个数的负数,所以为了为了让计算机硬件的设计简单,就将符号位参与了运算,这样就不用考虑减法的问题。
这里有牵扯到另外一个问题:为什么不使用减法,要用加法来替代?
原因也很简单,在减法运算中
例如:在正数减正数的过程中
了判断最终结果是正数还是负数,计算机需要先进行判断被减数和减数的大小关系,然后才能做出计算。
所以为了让计算机底层的运算与设计过程更加的简单,这帮计算机大佬们以让符号位参与运算为思路,设计出了 原码、反码和补码,