计算机怎么实现减法?
我一直对计算机存储和处理信息的方式很好奇,初中的时候无意在知乎上看到了计算机存储整数的科普,了解到计算机内部是用二进制表示数据的,并且十进制的加法在计算机中的处理方式也是二进制加法,从那时起我就一直有一个疑问,计算机是怎么实现减法的呢?
在回答这个问题之前,我们先要更深入地了解信息在计算机内部的存储方式。
位和字节
我们把计算机的内存想象成一大片格子,每个格子都只能存放1、0中的一个数,这样一个单独的格子就叫“位”。多个位连在一起就组成字节,一般计算机上一个字节由八个位组成。
单个格子能存储的信息非常有限,只有0或1。然而当我们把多个格子,也就是位连在一起并且赋予这些位某种意义的时候,这些位就能表示大量信息。比如我们考虑八个连在一起的位,将这些位称作长度为8的位向量 = ,就可以表示数集{7,6,...0}的任何子集E。当时,令,利用这种编码方式,就可以用位向量[0,0,0,1,0,1,0,0]表示数集{2,4}。
计算机中整数的编码
首先,我要请读者一定要把编码这个概念谨记于心,编码:指用代码表示各种信息,在计算机科学中,就是把信息转换成可以理解的二进制值!记住了编码,再来看接下来的内容。
整数由正整数、零和负整数组成。我们先来讨论零和正整数的编码方式。零和正整数,也就是无符号数的编码方式比较简单。我们还是考虑长度为8的位向量 = ,【位向量:你可以模糊地理解成一个二进制值】只需要这个位向量对应的二进制数直接转换成十进制,就可以得到对应的无符号数。下面给出这一编码方式的数学定义:
w代表位向量的长度。举个例子,长度为3的位向量所编码的无符号数是
我们很容易可以考虑到,这种编码方式所能编码的整数范围是有限的,并且这个范围和长度w是正相关的。
长度为w的位向量所能编码的最大无符号数
最小无符号数自然是0。
讲完了无符号数的编码方式,我们再来讨论有符号数,即正整数、零和负整数组成的数集的编码方式。
计算机发展历史中,一共出现过三种把位向量编码为负整数的编码方式,分别为原码、反码和补码,目前几乎所有计算机底层都是使用补码编码有符号数。大部分人第一次接触到原码、反码补码时都会一头雾水,不明所以,至少我身边的同学是这样。私以为,原因无他,就是国内对这三个名词翻译太离谱了,就算只写出Sign-Magnitude,Ones'Complement,Two's complement,直接不翻译,相信正常人查下字典都能把这三个术语的意思弄明白个大概。Sign符号,Magnitude大小,那自然就是符号值嘛!后面两个可能有点奇怪,但是随后等我介绍完反码和补码的定义后,你自会发现,英文名的命名逻辑比中文名的正常清晰多了。
原码
扯远了,下面先来介绍最早出现的原码。我们很容易可以想到一种用位向量编码有符号数的方式,即以最高位,即【看不懂这个的回过头去看位向量的定义】表示正负,1为负,0为正,剩下的w-1位正常编码。乍一看这种编码方式很方便,好像没有问题啊,但是其实问题多了去了啊!
(原码的数学定义)
我们先回过头来想想,把位向量编码为整数值要解决什么问题?
1.提供一种转换方式,让计算机可以存储整数数据。
2.这种转换方式,要能让计算机以一种统一、逻辑自洽的方式处理整数数据,通俗地讲,就是计算机在处理整数运算之类的数据处理时,要符合逻辑,不能乱来。
那么原码这种编码方式有什么错误呢?首先,以长度为4位的位向量为例,-1这个数按照我们刚刚的编码方式被解释为1001,1被解释为0001,-1+1理应等于0,但是计算机只能执行二进制加法啊,所以-1+1会先被以原码编码转换为二进制数,再执行运算,最后1001+0001的结果是1010,这显然是错误的。
除此之外,原码奇特的编码方式还会带来一个问题,即会出现+0和-0,10代表-0,00代表+0,但在数学中,+0、-0显然是不存在的。为了修补原码带来的这些问题,计算机内部还要添加额外的物理结果,或者执行额外的运算,我们当然不想出现这种结果,因此,反码登上了历史舞台。
反码
我们仍以长度为4位的位向量为例。怎么解决原码中出现的4+-4不等于0的问题呢?考虑下面一个等式:
是不是隐隐约约地觉得,这个等式和我们要实现的-x+x = 0还有点儿像?
如果有一种编码方式把[1111]-x编码为-x,那么自然,[1111]就能会被编码为0,上述等式就变成了-x+x = 0
反码,就是这样的一种编码方式。现在我们知道了反码的由来,就可以回答刚刚留下来的那个问题啦,为什么反码要叫Ones' Complement? 这个命名是基于,在反码编码中,【-x的二进制是以[1111....]-x,即2的w-1次方-1 -x,表示的】,所以是One,而且后面还有s(注意,补码的英文名字是Two's Complement,s不在Two的后面),complement补,是不是很形象?
反码的数学定义:
举个例子,长度为3的位向量 以反码编码的有符号数是
再举个例子,帮助大家理解反码编码-x的定义,x=4,以长度为5的位向量编码,那么-4的二进制表示应该是[11111]-4,也就是31-4,即27,再转换为二进制,就是11011!
再验证一下反码编码的正确性,-4+4,被转换为11011和00100,在计算机内部执行加法,11011+00100 = 11111 ,而11111在反码编码中被解释为0!
简直太神奇了!
但是,反码仍是存在缺陷的。在反码中,[1111...]和[0000....]被同时编码为0,这个问题仍没有解决,反码仍然是无法逻辑自洽的。
补码
最后,补码的出现解决了遗留下来的问题。在介绍补码的概念之前,计算机中表示数据所用的位数是固定的,如果计算机表示数据所用的位数是4位,超过4位的整数编码就会被强制转换为4位。利用这一性质,仍以4位为例,我们可以得到一个式子:
像在反码编码中我们做的一样,我们把-x的补码编码表示为[1111]+[0001]-x的二进制值,x表示为x的二进制值:
得到如下的数学定义:
补码和反码的唯一区别就是,-x w-1的乘数是2的w-1次方,而不是2的w-1次方-1。
现在我们也可以解答上面留下来的问题了,为什么补码的英文名是Two's complement?这实际上是基于:【-x的二进制是以[1111....]+[000..1]-x,即2的w次方减去x,表示的】
补码充分地利用了计算机存储信息时会【溢出】的特性,提供了一个逻辑自洽的体系供计算机执行整数的存储和处理。
讲了那么多,相信你已经知道计算机是怎么执行减法的了。把负数用补码编码,直接化为加法运算。
至于为什么不再在计算机内部设置一个和加法器类似的减法器呢?当然是因为麻烦呗!给数据编码是无法避免的计算消耗,如果在编码过程中就能把减法解决,那么为什么要再花费更多的资源去添加减法器呢?
写在最后
大一新生第一次写技术类博客,如有疏漏,烦请指出~~~~~