专栏/原码、反码、补码的深入理解(认真看完就能学会了)

原码、反码、补码的深入理解(认真看完就能学会了)

2022年12月16日 03:57--浏览 · --点赞 · --评论
咖啡煮泡面
粉丝:20文章:8

说明:这篇文章是up在网上的一篇文章的基础上进行备注与修改的。up给这篇原文加上了相关的备注(知识内容上的补充与讲解)以及一些错字,别字的修改(让原文更通顺,更能看懂。),另外还根据up自身所学到的知识给原文加上了【知识前要】这个内容。

有误之处,还请各位大佬在评论区说出来[抱拳]。

文中[李+数字]对应着位于本章末尾up加上的注析

知识前要:

编码分为:   ①数制编码:表示数量的规则

②码制编码:表示事物的规律

③混合编码:同时由数制和码制编码组成的编码

           

二进制编码即为机器码,机器只能识别并运行二进制编码,所以并不能使用正负号来区分数制编码的正负属性,而只能引入“符号位”来作注(“0”表示正;“1”表示负)。

那么问题来了:“符号位”上的编码是码制编码,即此时的编码变成了混合编码,无法进行正常的数学运算(加减乘除的运算本来就是针对数制型编码的)[李1] 。为了能让其能正常进行数学运算,便有了“反码”、“补码”的陆续出现。

将原码转换成补码的方法如下:

对于这个方法的解释其实很简单,文末有解释:

当然,使用“徒手求解”的方式来求出一个数的补码能更好地证明这个方法,不过需要学习完后文内容后才能领悟。

【注意:图中使用了五位二进制格式,徒手算时要使用“100000”来计算】

 

正文

原码,反码,补码的深入理解与原理

blog.csdn.net 2019-07-03 10:37

      本文从原码讲起。通过简述原码,反码和补码存在的作用,加深对补码的认识。力争让你对补码的概念不再局限于:负数的补码等于反码加一。

接触过计算机或电子信息相关课程的同学,应该都或多或少看过补码这哥仨。每次都是在课本的最前几页,来上这么一段:什么反码是原码除符号位,按位取反。补码等于反码加一。然后给整得莫名其妙,稀里糊涂地,接着就是翻页,反正后面的内容也跟三码没多大关系。

我原来也是看了好几遍都没看懂。古人云:事不过三。学C语言的时候,看过一次。不懂?看《计算机基本组成原理》的时候看过,还是不懂!到了大三,上《单片微机原理与接口技术》的时候仍旧是不懂。到了期末,复习的时候,和宿舍的人瞎聊。说讲讲这些码呀,我说我也不是很清楚呀。然后就一边说怎么求码,一边算。玩着玩着,突然就明白了。我说好,打住。不说了,放假我再好好整理下思路,于是就有了这篇额。。算讨论帖吧。

好了,废话不多说。开始我们的原码,反码,补码之旅。

(一)预备知识

认识二进制,十六进制。会二进制与十进制的相互转化运算由计算机的硬件决定,任何存储于计算机中的数据,其本质都是以二进制码存储。

根据冯~诺依曼提出的经典计算机体系结构框架。一台计算机由运算器,控制器,存储器,输入和输出设备组成。其中运算器,只有加法运算器,没有减法运算器(据说一开始是有的,后来由于减法器硬件开销太大,被废了 )

所以,计算机中的没法直接做减法的,它的减法是通过加法来实现的。你也许会说,现实世界中所有的减法也可以当成加法的,减去一个数,可以看作加上这个数的相反数。当然没错,但是前提是要先有负数的概念。这就是为什么不得不引入一个该死的符号位。

而且从硬件的角度上看,只有正数加负数才算减法。

正数与正数相加,负数与负数相加,其实都可以通过加法器直接相加。

原码,反码,补码的产生过程,就是为了解决,计算机做减法和引入符号位(正号和负号)的问题。

本文可能比较长,没必要一下子读完。原码,反码,补码,按章读。 重点在于讲补码,到了补码可能有些绕,建议带着笔,写出二进制数一起算。

表达可能不够清楚严谨,望见谅。

(二)原码

原码:是最简单的机器数表示法。用最高位表示符号位,‘1’表示负号,‘0’表示正号。其他位存放该数的二进制的绝对值。

若以带符号位的四位二进值数为例

1010 : 最高位为‘1’,表示这是一个负数,其他三位为‘010’,

即(0*2^2)+(1*2^1)+(0*2^0)=2(‘^’表示幂运算符)

所以1010表示十进制数(-2)。

下图给出部份正负数数的二进制原码表示法

OK,原码表示法很简单有没有,虽然出现了+0和-0,但是直观易懂。 于是,我们高兴的开始运算。

0001+0010=0011 (1+2=3)OK

0000+1000=1000 (+0+(-0)=-0) 额,问题不大

0001+1001=1010 (1+(-1)=-2)

噢,1+(-1)=-2,这仿佛是在逗我呢。

于是我们可以看到其实正数之间的加法通常是不会出错的,因为它就是一个很简单的二进制加法。

而正数与负数相加,或负数与负数相加,就要引起莫名其妙的结果,这都是该死的符号位引起的。0分为+0和-0也是因他而起。[李2] 

所以原码,虽然直观易懂,易于正值转换。但用来实现加减法的话,运算规则终归是太复杂。于是反码来了。

(三)反码

我们知道,原码最大的问题就在于一个数加上他的相反数不等于零。

例如:0001+1001=1010 (1+(-1)=-2) 0010+1010=1100 (2+(-2)=-4)

于是反码的设计思想就是冲着解决这一点,既然一个负数是一个正数的相反数,那我们干脆用一个正数按位取反来表示负数试试。

反码:正数的反码还是等于原码;

负数的反码就是他的原码除符号位外,按位取反。

若以带符号位的四位二进制数为例:

3是正数,反码与原码相同,则可以表示为0011

-3的原码是1011,符号位保持不变,低三位(011)按位取反得(100)

所以-3的反码为1100

下图给出部分正负数的二进制数反码表示法

0001+1110=1111 (1+(-1)= - 0)

互为相反数相加等于0,解决。虽然是得到的结果是1111也就是-0[李3] 

好,我们再试着做一下两个负数相加

1110(-1)+1101(-2)=1011(-4)

噢,好像又出现了新问题

(-1)+(-2)=(-4)?

不过好像问题不大,因为1011(是-4的反码,但是从原码来看,他其实是-3。巧合吗?)

我们再看个例子吧

1110(-1)+1100(-3)=1010(-5)

确实是巧合,看来相反数问题是解决了,但是却让两个负数相加的出错了。[李4] 

但是实际上,两个负数相加出错其实问题不大。我们回头想想我们的目的是什么?是解决做减法的问题,把减法当成加法来算。

两个正数相加和两个负数相加,其实都是一个加法问题,只是有无符号位罢了。而正数+负数才是真正的减法问题。

也就是说只要正数+负数不会出错,那么就没问题了。负数加负数出错没关系的,负数的本质就是正数加上一个符号位而已。

在原码表示法中两个负数相加,其实在不溢出的情况下结果就只有符号位出错而已[李5] (1001+1010=0011)

反码的负数相加出错,其实问题不大。我们只需要在实现两个负数加法时,将两个负数反码包括符号位全部按位取反相加,然后再给他的符号位强行置‘1’就可以了。[李6] 

所以反码表示法其实已经解决了减法的问题,他不仅不会像原码那样出现两个相反数相加不为零的情况,而且对于任意的一个正数加负数,如:0001(1)+1101(-2)=1110(-1) 计算结果是正确的。所以反码与原码比较,最大的优点,就在于解决了减法的问题。

但是我们还是不满足为什么 0001+1110=1111 (1+(-1)=-0) 为什么是-0呢

而且虽然说两个负数相加问题不大,但是问题不大,也是问题呀。好吧,处女座。接下来就介绍我们的大boss补码。

(四)补码

补码:正数的补码等于他的原码,负数的补码等于反码+1。 (这只是一种算补码的方式,多数书对于补码就是这句话)

在《计算机组成原理中》,补码的另外一种算法是

负数的补码等于他的原码自低位向高位[李7] ,尾数[李8] 的第一个‘1’及其右边的‘0’保持不变,左边的各位按位取反,符号位不变。

OK,补码就讲完了。再见!!

还是莫名其妙有没有,为什么补码等于反码加1,为什么自低位向高位取反...................?

其实上面那两段话,都只是补码的求法,而不是补码的定义。很多人以为求补码就要先求反码,其实并不是。

那些鸡贼的计算机学家,并不会心血来潮的把反码+1就定义为补码。只不过是补码正好就等于反码加1罢了。

所以,忘记那些书上那句负数的补码等于它的反码+1。就是这句话把我们带入了理解的误区。

这就是后来我明白为什么我看的那本《计算机组成原理》,要特意先讲补码,再讲反码。[李9] 

然后说负数的补码等于他的原码自低位向高位,尾数的第一个‘1’及其右边的‘0’保持不变,左边的各位按位取反,符号位不变。

但是上面这句话,同样不是补码的定义,它只是补码的另外一种求法。它的存在,告诉我们忘记那句该死的‘反码+1’,它并不是必须的。

如果你有兴趣了解,补码的严格说法,我建议你可以看一下《计算机组成原理》。它会用‘模’和‘同余’的概念,严谨地解释补码。

接下来我只想聊聊补码的思想。

(五)补码的思想

补码的思想,第一次见可能会觉得很绕,但是如果你肯停下来仔细想想,绝对会觉得非常美妙。

补码的思想其实就来自于生活,只是我们没注意到而已。时钟,经纬度,《易经》里的八卦。

补码的思想其实就类似于生活中的时钟

好吧,我其实不想用类似,好像这种词,因为类比的,终究不是事物本身。而且不严谨会让我怀疑我不是工科僧,说得好像我严谨过似的,哈哈

如果说现在时针现在停在10点钟,那么什么时候时针会停在八点钟呢?

简单,过去隔两个小时的时候,是八点钟。未来过十个小时的时候也是八点钟

也就是说时间正拨10小时,或是倒拨2小时都是八点钟。

也就是10-2=8,而且 10+10=8(10+10=10+2+8=12+8=>8)

这个时候满12说明时针在走第二圈了,又走了8小时,所以时针正好又停在八点钟。

所以“12”这个数值在时钟运算中,称之为模,超过了12就会重新从1开始算了

也就是说, 10-2和10+10从另一个角度来看是等效的,它都使时针指向了八点钟。

既然是等效的,那在时钟运算中,减去一个数,其实就相当于加上另外一个数(这个数与减数相加正好等于12,也称为同余数)

这就是补码所谓模运算思想的生活例子

在这里,我们再次强调原码,反码,补码的引入是为了解决做减法的问题[李10] 。在原码,反码表示法中,我们把减法化为加法的思维是减去一个数,等于加上一个数的相反数,结果发现引入了符号位,却因为符号位造成了各种意想不到的问题。

但是从上面的例子中,我们可以看到其实减去一个数,对于数值有限制,有溢出的运算(模运算)来说,其实也相当于加上这个数的同余数。

也就是说,我们不引入负数的概念,就可以把减法当成加法来算。所以接下来我们聊4位二进制数的运算,也不必急于引入符号位。因为补码的思想,把减法当成加法时并不是必须要引入符号位的。

而且我们可以通过下面的例子,也许能回答另一个问题,为什么负数的符号位是‘1’,而不是正数的符号位是‘1’。

(六)补码实例

好吧,接下来我们就做一做四位二进制数的减法吧(先不引入符号位)

0110(6)-0010(2)【6-2=4,但是由于计算机中没有减法器,我们没法算】

这个时候,我们想想时钟运算中,减去一个数,是可以等同于加上另外一个正数(同余数)

那么这个数是什么呢?从时钟运算中我们可以看出这个数与减数相加正好等于模。

那么四位二进制数的模是多少呢?也就是说四位二进制数最大容量是多少?其实就是2^4=16=10000(2)­[李11] 

那么2的同余数,就等于10000-0010=1110(14)

既然如此

0110(6)-0010(2)=0110(6)+1110(14)=10100(20)

OK,我们看到按照这种算法得出的结果是10100,但是对于四位二进制数,最大只能存放4位(硬件决定了),如果我们低四位,正好是0100(4),正好是我们想要的结果,至于最高位的‘1’,计算机会把他放入psw寄存器进位位中。8位机则会放在cy中,x86会放在cf中(这个我们不作讨论)

这个时候,我们再想想在四位二进制数中,减去2,就相当于加上它的同余数14(至于它们为什么同余,还是建议看《计算机组成原理》)

但是减去2,从另外一个角度来说,也是加上(-2)。即加上(-2)和加上14其实得到的二进制结果除了进位位,结果是一样的。

如果我们把1110(14)的最高位看作符号位后就是(-2)的补码[李12] ,这可能也是为什么负数的符号位是‘1’而不是‘0’[李13] 

而且在有符号位的四位二进制数中,能表示的只有‘-8~7’,而无符号位数(14)的作用和有符号数(-2)的作用效果其实是一样的。[李14] 

那正数的补码呢?加上一个正数,加法器就直接可以实现。所以它的补码就还是它本身。

下图给出带符号位四位二进制的补码表示法

在补码中也不存在负零了,因为1000表示-8

这是因为根据上面的补码图,做减法时,0001(1)+1111(-1)=0000 我们再也不需要一个1000来表示负0了,就把它规定为-8

负数与负数相加的问题也解决了1111(-1)+1110(-2)=1101(-3)

可能说得有点绕,但是实在是没办法。其实我觉得补码还可以这样画。

(七)为何这样求补码

然后我们再来看看为什么负数的补码的求法为什么是反码+1

因为负数的反码加上这个负数的绝对值正好等于1111,再加1,就是10000,也就是四位二进数的模。

而负数的补码是它的绝对值的同余数,可以通过模减去负数的绝对值,得到他的补码。

所以 负数的补码就是它的反码+1。[李15] 

有点绕吧,只能说很难算清楚,你们还是自己算算吧。还有上面我提到的另外一种算法。

接下来,我要说一下我自己算补码的小技巧。

看上面那个图。

如果我们把-8当成负数的原点。那么-5的补码是多少呢?[李16] 

-5的补码就是-8的补码加3

所以完全可以口算出-5的补码是1011

当然,也可以记住-1的补码是1111口算减法得出

对于八位加法器的话,可以把-128当补码原点。十六位可以把-32768当补码原点。

是的,128是256(八位二进制数的模)的一半,32768是65536(十六位二进数的模)的一半

也很方便有没有,而且简单的是

补码原点总是最高位是‘1’,其他位是‘0’

所以做加法总是简单得可以口算。

OK,原码,反码,补码之旅就到这里结束。补码第一次看总会觉得很绕,想言简意赅,就怕哪里遗漏了。讲得细致,又不免连自己都觉得啰里啰嗦。谢观

原文转载自:https://www.imooc.com/article/16813?block_id=tuijian_wz

 


 [李1]加减乘除运算本来就是针对数制型编码的,因为数制型表示的是大小,是数量;而码制型编码表示的是逻辑,所以在这种加入了符号位的原编码(原码)就注定会因为其内部本质为码制的符号位的存在而无法正常地进行计算。


 [李2]正如前文的批注所说,原码的本质注定了其无法正常进行运算的结果。


 [李3]另外,通过验证我们可以发现:

在非符号位不溢出到符号位的情况下,反码可以满足正数与负数的加法的正确运算。


 [李4]【原码的问题共有两个:正数与负数相加,或负数与负数相加,就要引起莫名其妙的结果】

 


 [李5]指非符号位不溢出到符号位上


 [李6]说白了就是:

在确保运算过程中非符号位不会溢出到符号位的前提下,在进行反码的负数加法时,先将其非符号位转换成原码进行加法运算,然后再将符号位改成1。


 [李7]中最右边的是最低位,标记为第0位,向左边一位是第1位,以此类推。


这里的“尾数” [李8]不同于如“二进制小数”、“十进制小数”、“十六进制小数”里的“尾数”。“XX进制小数”的尾数指的是小数点往右的数,比如“101.11”里的“11”两位数。而文章这里的“尾数”则指的是除去符号位之后的数字,这些数字以低位到高位为顺序。所以,所谓的“第一个‘1’”就是最低位的那个数字。但有时我们会遇到一种比较特殊的情况,那就是尾数第一位就是1,那么就会有疑问:“那么哪来的‘右边的0’?”因为再往右都没有数字的了嘛,这个情况我们直接忽略那个“0”就行,既然没有,那就不管了。

我们可以简单验证一下:

例1.

原码:10=01010;-10=11010

补码:-10=10110

例2.

原码:8=01000;-8=11000

补码:-8=11000

显然是成立的。


 [李9]也就是说作者从《机组》的授课顺序里明白了“要求补码,就要先求反码”是错误的。


 [李10]回顾一下:

原码因其自身本质的约束无法正常进行运算。会出现负零,无法满足除正数间的加法运算外的其他一切运算;

反码能正确地实现正数与负数相加,但无法正确运算负数与负数相加(但可以在非符号位不溢出到符号位的情况下使用“运算后强行置‘1’”的特殊方法进行运算)。但是,反码解决了在非符号位不溢出到符号位的前提下正数与负数的相加问题。


 [李11]对于这个地方的解释:

为什么是10000?

首先,四位二进制数一共能表示2^4种“0”“1”排列。但其实不难发现:当在“二进制数”这个角度(用二进制数来表示1~16)去看这十六个组合的时候,我们会发现“0000”这个组合只能由“10000”来表达。在运算时,也就是要求同余数的时候就只能用10000来运算,所以必须要用10000。

对于另一方面的问题:为什么用四位二进制数所能表达的排列种数来作为四位二进制数的最大容积,即其“模”的数值呢?

答案其实就是:这十六种可能不就对应着四位二进制数能表达出来的十六个数嘛。

可能还会有人问,为什么就是不能让“0000”“单独存在”?

这个问题很好回答。我们现在是要找到一个合适的二进制数作为“模”,而我们希望这个数是能进行同余数计算的。这样看来,选“10000”自然就比选“0000”合理得多了。


 [李12]现在由所学可知

14的原码:1110

-2的原码:1010

前面用模的方法求出了(-2)的同余数就是14;进而我们知道了减2就等于加上(-2)且等于加上14。基于要将减法当成加法来计算的前提,那么我们将这个14的原码表示定义为(-2)的补码表达形式,所以也就有了补码的表示在本质上的定义。总结一下:

其实补码就是使用了“模”和“同余数”这两个概念,利用多位二进制溢出计数的特点(即超出一定数值后会“归零”这一特点

),定义出了用减数绝对值的同余数来定义这个减数原码的另一种表达形式,也就是所谓的“补码“。


 [李13]只能作为一种解释的方式,毕竟“符号位”这个概念出现得比补码要早。


 [李14]如此重要的内容压缩为几字之少,着实让人费解。

对于补码的计算里这一点也是非常重要的一点!

首先,上了清华大学王红副教授的课会了解到:不同于原码和反码,补码的符号位是有权值的(且保留其正负属性,“0”的时候取正,“1”的时候取负),其存在不单单是正负的标识符,这也被用于解释为什么补码这一有符号位的编码只能表示-8到7这个范围的数。

为什么会这么特殊?

究其真相其实就是为了能更好地解释补码。让我们能够根据补码来算出对应的十进制数。

学会了根据补码的本质来“徒手”求出补码,就会发现求出一个数的补码后,如果正常地按照二进制数转换成十进制数的方法来计算,会发现行不通。根本就没法算。

举个例子:

-5的补码为1011

按照正常的转换运算,我们得到:(1*2^3)+(0*2^2)+(1*2^1)+(1*2^0)=8+0+2+1=11

这显然不对

但是,如果我们将符号位视作有权值的话,运算就会变成:【1*(-2)^3】+(0*2^2)+(1*2^1)+(1*2^0)=(-8)+0+2+1=-5

这样,就能正确地计算出对应的十进制数了。


 [李15] 解释:

我们就拿数字“6”来举例说明

假设我们现在要做一个减法式子——a-6(a>0)

首先我们以补码的方式来看就是将式子看成“a+6的同余数”。而同余数是怎么求的?就是用模减去“6”本身嘛。也就是说:

a-6=a+6的同余数=a+(模-6)

然后我们以反码的角度来看式子:

a-6=a+6的反码=a+1001

【6的原码:0110;-6的反码:1001(符号位置“1”,尾数全部置反)】

我们可以发现的就是:6的原码+(-6的反码)=1111,同时1111+1=10000=模

所以我们得到:

模=6的原码+6反码+1

综上所述,很明显,补码=这个负数的反码+1


 [李16]这个其实很好算

问题其实就是:

已知-8的补码,求-5的补码

这个问题本身就是负数补码内的思维范畴了

研究一下上文中的补码表,我们就能发现各个相邻的补码之间都相差1。所以,知道了-8的补码,那么-5的补码就是-8的补码加3


投诉或建议

装修网金毅盛装饰方圆装修有限公司什么装修材料好重庆一家装饰口碑装修简装北京市家庭装修合同杭州房子简装御华晓风江南是简装还是精装汽车美容及装潢吴江装修公司哪家好巴洛克式装修公装设计工程成都简装房屋别墅不装修的住法50平 装修正规装修哪家好长沙公装企业门店的装修日本简装6月装修好吗墨绿色装修风格2021年卫生间装修原阳140平简装多少钱电商直播间装修常德欧式风格装修郑州公装法国绿装修昆山一般装修多少钱精装修装修清骏装饰香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声汪小菲曝离婚始末卫健委通报少年有偿捐血浆16次猝死单亲妈妈陷入热恋 14岁儿子报警雅江山火三名扑火人员牺牲系谣言手机成瘾是影响睡眠质量重要因素男子被猫抓伤后确诊“猫抓病”中国拥有亿元资产的家庭达13.3万户高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了男孩8年未见母亲被告知被遗忘张家界的山上“长”满了韩国人?倪萍分享减重40斤方法许家印被限制高消费网友洛杉矶偶遇贾玲何赛飞追着代拍打小米汽车超级工厂正式揭幕男子被流浪猫绊倒 投喂者赔24万沉迷短剧的人就像掉进了杀猪盘特朗普无法缴纳4.54亿美元罚金周杰伦一审败诉网易杨倩无缘巴黎奥运专访95后高颜值猪保姆德国打算提及普京时仅用姓名西双版纳热带植物园回应蜉蝣大爆发七年后宇文玥被薅头发捞上岸房客欠租失踪 房东直发愁“重生之我在北大当嫡校长”校方回应护栏损坏小学生课间坠楼当地回应沈阳致3死车祸车主疑毒驾事业单位女子向同事水杯投不明物质路边卖淀粉肠阿姨主动出示声明书黑马情侣提车了奥巴马现身唐宁街 黑色着装引猜测老人退休金被冒领16年 金额超20万张立群任西安交通大学校长王树国卸任西安交大校长 师生送别西藏招商引资投资者子女可当地高考胖东来员工每周单休无小长假兔狲“狲大娘”因病死亡外国人感慨凌晨的中国很安全恒大被罚41.75亿到底怎么缴考生莫言也上北大硕士复试名单了专家建议不必谈骨泥色变“开封王婆”爆火:促成四五十对测试车高速逃费 小米:已补缴天水麻辣烫把捣辣椒大爷累坏了

装修网 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化