前言
计算机只认识0、1,而原、反、补码的存在是为了解决整数数值的存储和计算问题。在后面的介绍中我们将了解,原、反、补码的作用以及原理。
原码-整数的二进制表示方法
由于计算机的电气特性,整个计算机内部被设计为类似开关形式的电路,而开关无非就两种状态:开/关。为了方便计算机处理整数,所以二进制形式的整数更容易被计算机处理。
整数分为正整数(1、2、3、4、5...),负整数(-1、-2、-3、-4、-5...)和零0,而且整数一般指十进制的数(满十进一),十进制数与二进制数存在的关系如下:
我们能够将一个十进制的数如17,除2得到商和余数,然后再将结果再除2,直到被除数为0为止,我们依次从左到右的可以得到10001。
可以看出原码只是十进制整数的一种表现形式,在计算机中,我们通常会把存储0或1的成为最小单位bit,而且会以8bit为一组成为1个字节(Byte),存储各类数据的时候就是通过许许多多的Byte的组合来完成的,我们之前说过整数是有正负之分的,那我们不妨以1开头表示负数,以0开头表示整数,那么如果我们只有4bit,除去开头1bit来表示符号位,还有3bit能够表示数值位,3bit能够表示的可能为000 - 111,十进制对应的为0-7,那么加上符号位,可能组成的十进制数为-7到+7,并且会出现-0和+0这两种都表示0的情况,同理如果是8bit,那么能够表示的十进制数就为-127到127,说到这里我们认识了原码,它能够表示整数,但是我们会发现这种码用来表示整数不够完美,因为有-0和+0,都表示0还有正负之分,实在是有点冗余了。
最后我们来看看原码能够表示的范围与位数之间的关系吧,很明显能够表示的整数范围与bit位数相关,假设我们有n bit,那么能够表示的范围就是:
-(2^(n-1) - 1) ~ 2^(n-1) - 1,n > 0 且 n ∈ Z
补码-基于同余定理的另一种表示方法
因为反码只是求解补码的一个中间过程,所以就不再重复撰述了,接下来将说明这一切缘由。
首先我们先来看看十进制的加法和原码形式的二进制的加法:
十进制加法与二进制加法是一样的,只不过进位条件不同,一个满十进一,一个满二进一。这么看着好像二进制可以直接用原码来操作。
接下来介绍减法操作,在这之前需要先给大家普及一下知识,计算机内部或者说CPU只实现了加法器,这个主要是为了硬件电路的优化,如果通过加法器既可以做加法运算又可以做减法运算那不是两全其美嘛。那么也就意味着计算机会将7 - 3当作是7 + (-3),来看下面这张图:
我们发现通过原码进行计算7 - 3 的结果竟然得到-10,竟然是一个负数很明显不对呀,难道之前说的都是鸡同鸭讲嘛?那么现在问题来了,计算机在进行二进制形式的减法运算的时候不能够使用原码来进行了,因为结果完全就是扯犊子。那么我们怎么样才能找到一种,能够将用加法来代替减法的二进制运算方法呢?这是我们接下来要探讨的问题。
首先我们来看看这张时钟图,我们脑海中想象一下,时钟的时针从0刻度开始顺时针旋转,依次从0到11,当达到11点钟的时候,指针又回到了0刻度的位置,我们把这种性质叫做模mod的性质。在这个时钟模型里,模是12,说了半天这个模的概念到底有什么用?
通过这个模,我们能够知道在满足“时钟计数“的规则下,还可以知道在这个时钟上任意一个时间点。比如说3,我们从0开始计数可以发现顺时针需要移动三格指针记为+3,而我们从0开始逆时针旋转9格指针记为-9会发现,最终的结果都会来到刻度3这个位置。那么我们就可以列出取模的等式了,(0 + 3) mod 12 = 3并且(0 - 9) mod 12 = 3(mod我们表示取模运算符)。
我们发现什么了?0 - 9竟然和0 + 3等价,这不就是我们要找的加法代替减法的运算方法嘛?同样的如果我们从任意一点开始顺时针计算到3的距离也是能够找到对应的一个负数(表示逆时针)来形成(A - B) mod C = D,并且(A + E) mod C = D的等价式子,一般我们称B与E互补,且B + E = 模大小(这个就是同余定理),比如再举一个例子从1开始,可以得到(1 + 2) mod 12 = 3,(1 - 10) mod 12 = 3。
再来看下面这张图继续来感受一下:
首先我们先假设我们只有4bit,一共能够存储的二进制数个数为16个,分别为0000到1111,我们称其为码。而图中在码的上方对应的数,有正负之分,我们认定负数就是逆时针,正数就是顺时针,而数字表示移动的刻度,我们就得到 数 => 码,码 => 数的关系,而且我们能够看出码的运算非常符合我们的二进制加法运算,比较有意思的是1111 => 0000的情况,这里我们认为只有4bit的情况1111 再累加1时发生进位,而最多只能够存储4bit所以会产生溢出,导致结果变为0000,通过丢弃溢出位刚好保证和时钟的计时类似。我们再来看看数的规律,如-1我们用原码表示的话应该是1001,但是通过上面的转化关系我们能够得到1001 与 1111存在某种转化关系(其实把两者一相加就是1000刚好就是模的值),那么这种转化关系是什么呢?我们能够将所有的情况列出来(正数就不列举了,都是相同的):
数和原码 | 图中数对应的码 |
-1(1001) | 1111 |
-2(1010) | 1110 |
-3(1011) | 1101 |
-4(1100) | 1100 |
-5(1101) | 1011 |
-6(1110) | 1010 |
-7(1111) | 1001 |
-8(不存在原码) | 1000 |
其实我们不然发现通过负数的原码,想要得到图中数对应的码,是需要保持符号位不变,数值位取反再+1的,这就是咱们所谓的获取补码的方式。
比较有意思的是-8这个数,在原码中我们能够知道如果是4bit,那么我们能够拿到的是-0,而在上面那张图里我们能够将-0也表示成一个不同的数值,已经能够解决原码规则下出现冗余0的情况了。
说了这么多,那它是如何将减法变成加法的呢?再来看看,我们怎么利用这个图来将减法转化为加法的,已知:7 - 3 = 7 + (-3),7对应图中的码是0111,而-3对应的码是1101。则运算如下:
得到的结果为0100,在图中码 => 数的规则中对应的数为4,结果与十进制形式计算出来的一模一样。
再来看看补码的范围吧,假设我们有n bit,那么能够表示的范围就是:
-2^(n-1) ~ 2^(n-1) - 1,n > 0 且 n ∈ Z。