这几天想要开始学习系统的原理,本身硬件的系统已经下潜到底层与硬件的各个组成部分进行交互,例如:对CPU的资源管理;内存的管理等。

既然要研究底层,无非从最基础开始,二进制绝对是计算机的灵魂。二进制使用逻辑运算实现各种数的算数运算是学习二进制的基础,下面就来分析一下二进制的加减法的实现方式。

加法

回想一下,我们学习十进制加法的时候是先从1位数开始,这里我们也从1位数开始学习二进制的加法。

下面用真值表来表示1位二进制的加法。

X

Y

X+Y

进位

0

0

0

0

0

1

1

0

1

0

1

0

1

1

0

1

从表中我们可以看到实现1位二进制加法的基本元素有四个:加数X、加数Y、和X+Y(这里的和只有本位)、进位。

如果从函数的角度来看就是有两个输入,两个输出。

下面我们来分析一下从输入到输出的运算,注意二进制都是用逻辑进行运算,基本的逻辑运算有与、或、非三种。

  1. 从输入到和的运算是异或(数同为假,数异为真)。
  2. 从输入到进位的运算是与。

上面的1位加法属于半加器,因为我们没有考虑来自前面运算的进位,但实际上加上前面的进位运算也很简单,实际上就是用两个半加器结合来,各位可以自己去思考一下。

实现了一位全加器之后,就可以实现多位全加器了。

减法

减法可以转换成加一个负值,比如12 - 8,转换成12 + (-8),这两个是等价的。这就引出另一个问题,计算机怎么表示负数的?

按正常思路,可以用最高位代表符号,0代表正,1代表负,比如一个8位二进制:0000 0001是1,1000 0001是-1;这看起来很自然。但是你马上会发现:0000 0000和1000 0000这两个值出现矛盾了,从字面上理解这两个都是0,应该是相等的。但从内存上看这两个字节又是不相等的。

计算机先驱们用另一种方式存储负数,首先对于一个整型,我们要先明确它是有符号的还是无符号的,同样的存储,有符号和无符号代表的数值可能是不一样的,以一个8位数为例:

二进制  00000000 00000001 00000010...01111111 10000000 10000001...11111110 11111111
无符号         0        1        2........127      128      129........254      255
有符号         0        1        2........127     -128     -127.........-2       -1

有符号数到达127之后,再往后是-128,后面是-127,-126...一直到-1,这样8位有符号数的范围就是:-128~127。

虽然从二进制看一直在涨,但到10000000以后,其表示的含义却是从负数的最大值往下减。10000000,10000001这些二进制被称为负数的补码形式,所以计算机是用负数的补码来表示负数的。

负数的补码和对应的正数有一个奇妙的运算关系:

  • 对一个正数取反,再加1,会得到其负数的补码。
  • 对一个负数的补码取反,再加1,会得到其正数。

在计算机中,正数以正常方式存储,而负数是用补码的形式来存储的。

> 计算机内存| 00000111 | 11111001|
	   |     7    |      -7 |

如上所示,用补码存负数有一个好处,就是可以用加法代替减法。如下所示

5-3
=5+(-3)
=(00000101)+(11111101)
=(00000010)	正数
=2

 4-6
=4+(-6)
=(00000100)+(11111001)
=(11111101)	补码代表负数
=-2

参考:二进制的奥秘:用逻辑运算实现加减法