计算机如何进行加减法运算
困扰了我很久的问题,今天决心研究一下
什么是原码,反码,补码
说一下我们平时说的整数类型:
| 类型 | 长度 | 说明 |
|---|---|---|
| int8 | 8位 | 有符号整数,第1位表示符号,范围是[-2^7, 2^7-1] |
| uint8 | 8位 | 无符号整数,范围是[0, 2^8-1] |
| int16 | 16位 | 有符号整数,第1位表示符号,范围是[-2^15, 2^15-1] |
| … | … | … |
以下我们将以int8(有符号整数)为例子说明原码、反码、补码,数字后面的括号内部的n是数的n进制表示。
原码
一个数的二进制表达。 比如说6的原码是 0000 0110 ,而-6的原码为 1000 0110
反码
正数的反码就是他的原码。负数的反码是除去符号位(最高位)后其他位取反。比如6的反码为 0000 0110, 而-6 的反码为 1111 1001
补码
正数的补码是他的原码,负数的补码是在其反码的基础上+1。如6的补码为 0000 0110。 而-6的补码为 1111 1010
为什么需要反码和补码
事实上原码是我们最容易理解计算的,但是对于计算机来讲要想识别符号位然后对其数值做加减运算实现会非常复杂(计算机一般只实现了全加器的电路组件,如果还要设计全减器成本会很高)。于是人们将符号位也参与运算,这也是反码和补码出现的意义。
计算机的加法计算
计算机的加法计算相对比较好理解,举一个例子:
6(10) + 4(10) = 0000 0110(2) + 0000 0100(2) = 0000 1010(2) = 10(10)
有一个点要注意:
100(10) + 100(10) = 0110 0100(2) + 0110 0100(2) = 1100 1000(2) = 200(10)
我们发现当计算100 + 100 的时候得到的结果需要8位来表示,但我们知道int8类型的最高位表示符号,只有7位表示数值本身。这就出现问题了,当我们使用计算机计算的时候会发现得到了-56。
我们思考一下为啥捏?
计算机计算的结果是1100 1000(2),1被识别为符号为负数,剩下的100 1000则是56。
这就是溢出,此时我们需要使用更多的位数表示整数比如int16。
计算机如何将减法变为加法
我们只有全加器,怎么做减法呢?
答案就是轮回。
啥意思捏?
为了方便理解我们以10进制来解释,我们想象一下假设我们只有个位能表示0-9十个数字。
当我们计算9+1的时候发现需要十位才能表示10。
而我们只有个位,只能抛弃进位,此时个位又变成0了。
所以当发生进位的时候我们看到了轮回。
结论:在有限的位数里,如果两个数相加结果是0,我们称两个数互为补数。在上述的例子里 1和9互为补数,2和8互为补数。
在计算机里也就是我们说的补码。
那么如何得到补码呢?
计算机采用了一个很有意思的机制,反码+1。
下面我们来分析一下为啥反码+1就是补码了:
还是以10进制为例,1的补码是10-9,2的补码是10-8。但是我们只有一个位表示不了10,那么我们用9-原码+1就可以了。所以在计算机中1的补码是9-1+1,2的补码是9-2+1。
但是我们依然没办法算,因为还是上面还是要做减法。我们换个思路,不过这次我们要用2进制举例了(因为秘密在逻辑运算,而十进制没法逻辑运算)。
我们用uint8来说明,uint8能表示0000 0000(2)- 1111 1111(2)。
假设我要求 0000 0100(2) 的补码。计算方法是 1111 1111(2) - 0000 0100(2) + 0000 0001(2)
我们无法计算1111 1111(2) - 0000 0100(2),但我们可以找到0000 0100(2)跟谁相加等于1111 1111(2)
我们知道,在二进制中两位相加最后结果等于1,那么这两个位一定是相反的:
- 1 + 0 = 1
- 0 + 1 = 1
- 0 + 0 ≠ 1
- 1 + 1 ≠ 1
那么我们想要得到0000 0100(2)跟谁相加等于1111 1111(2),是不是只要将0000 0100(2)按位取反就可以了。
我们把二进制中一个数字按位取反的结果叫做反码。
结论:求一个数的补码只要求一个数的反码+1
#以int8的两个整数举个例子:
7(10) - 6(10)
= [0]000 0111(2) + [1]000 0110(2)
= [0]000 0111(2) + ([1]111 1001(2) + [0]000 0001(2))
= 0000 0111(2) + [1]111 1010(2)
= 1 0000 0001(2)
= 0000 0001(2)
= 1
由于发生了高位溢出所以丢掉进位,结果就是1
思考一下
如果两个数字相加的和会发生高位溢出,那么我们还能得到正确的结果吗? 以uint8为例,计算 200 + 100 结果是多少?为什么呢?
应用一下
在LeetCode中231题,2的幂其实可以利用位运算解决,其中涉及到了负数的补码表示
文档信息
- 本文作者:KcJia
- 本文链接:https://blog.kcjia.cn/2020/04/23/BaiscCode/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)