颠覆你的认知--重新认识原码、反码、补码

好吧,搁浅了一个多月的文章,总算竣工了。。。。。。。

本来一个多月前就准备写这篇文章了,那时候还在找工作,谁知道换了新工作后的这个月忙成🐶,周末都加班,你们想想有多忙!

说起来写这篇文章的初衷还是因为一次笔试,其中有一道求补码的题,然而我没做出来……讲真,笔试这种东西我觉得考考应届生可以的,没经验,那就看基础,但是对于工作过几年的人再不管三七二十一笔试题先奉上,多少都有点没诚意吧,当然最关键的不是笔试本身,而是笔试内容,全是一些概念理论和面试宝典上的题目,真的很想摔笔走人!

拿这道题目来说吧,没有难度可言,但是毕业后再没接触过,难免会忘记,所以不懂这些题的意义何在,当然了,对于大神们来说这些应该还是小儿科了,咱还是能力不足啊,这不,回头就补了下这块知识点。

原码,反码,补码

先出几个题吧(8位的情况下):

1. 127的原码、反码、补码分别是什么?
2. -127的原码、反码、补码分别是什么?
3. 0?
4. -128?
5. 128?

如果以上题目你都会了,那么,恭喜你,下一篇文章你可以关注下,也许有你还不会的;如果这些你都不会,那么老老实实的看下去吧……

首先介绍一下概念,其实也不算是概念,只是一种记忆方式:

正数的原码、反码、补码都是这个数的二进制表示,三个码是一样的!以下规则针对负数才有用!!!

原码:一个数的二进制的表示,最高位表示符号位,最高位0为正,1为负,也是我们最容易理解的一种方式

反码:将原码的所有二进制位取反,符号位不变

补码:反码的基础上加1

有了以上基础后我们可以来做题了。

第一题:127

127的三个码:首先127是个正数,所以,直接算出他的二进制01111111,也就是它的三码

第二题:-127

首先它是个负数,计算它的原码,只要将第一题127的符号位改成1就是了,所以-127的原码是11111111,反码是符号位除外取反,所以反码是10000000,补码是反码的基础上加1,所以补码:10000001,没毛病。

第三题:0

等等,0是个什么鬼啊?正数还是负数啊?算了算了,pass掉,下一题吧

第四题:-128

嗯,这个是负数,我会的,先求它的原码,也就是,也就是……好像哪里不对啊,好像没有这个数的原码啊,除去符号位,最大也就127了,一定是溢出了,对,肯定是这样的……不好意思,遗憾的告诉你,这个数在计算机里并没有溢出,因为计算机是用补码来表示数的,而-128确实能用补码表示出来,具体是什么下面再说,我们还是先看下一题吧。

第五题:128

!!!什么鬼,又来一个。好吧,别紧张,这个确实是溢出了。

做完以上题目是不是发现这些边界值好难记,好绕(其实是我在绕你们而已),为什么要这么绕,我觉得很多资料都是画蛇添足了,为了计算补码而多出来中间的反码和转换步骤,然后我们就乖乖的按着这个步骤计算就完事了,也不用关心是为啥,反正答案出来就行了。但这样的后果就是时间一长就忘了,特别是一些临界值和特殊值。

其实在计算机中或者说机器语言中是没有负数这个概念的,我们所说的负数和符号位只是为了我们人类的认知而自己定义的,对于计算机来说,只有正数,这是其一;其二,计算机中也没有减法,它只会做加法,当然这个加法也是人为的设定,当我们需要做减法的时候我们只能换成加上一个负数,因此也就有了在计算机中表示负数的需求,然后我们就规定最高位作为符号位并且参与运算,但计算的时候得先换成补码,这就是这些中间概念和过程的来源。
知道了补码的来历后我们还需要知道一个知识点,就是模。其实就是一个溢出舍去的过程,举个🌰:时钟一圈是360度,当然我们也可以用361度来表示,但实际上它和1度是一样,所以在这里模就是360度。那-30度呢?这种情况下我们逆时针转30度和顺时针转330度结果是一样的,所以我们就把-30变成了加330,这也是模的意义所在,利用模,可以将减法变成加法,可以将溢出部分舍去而不改变最终结果。

以上的概念只是我个人的理解,如果错误或者不合适的地方还请指出。

基于上面两个知识点,我们来看计算机中的形式。

假定一个前提:单字节的运算(也就是8位)
单字节运算中我们的模即是256(2^8),在没有符号位的情况下我们计算下127+2等于多少,结果是129,没有溢出,二进制表示为10000001,一切顺利。这时,如果我们将最高位当做符号位会怎么样呢?127+2结果仍然是10000001,那么结果是-1?要知道计算内部是用补码来表示数的,所以这个10000001是一个数的补码。那么我们倒推一下,明显这个是一个负数,负数转补码是原码取反加1,所以我们减1取反就是原码了,所以这个负数的原码应该是11111111-127!可以看一下题2作为验证。

绕了这么多,感觉自己都有点乱了,这里直接给出结论:

计算机中负数的补码就是模减去这个数的绝对值

-127这个负数,在计算机中就是129,也就是256-127 = 129。为什么正数的补码等于原码?因为计算机中可以直接表示正数。为什么负数的补码需要转换?因为计算机中没有负数,只能用另一种形式来表示负数,也就是通过模运算得出的一个相同作用的数,即我们所谓的补码。

回头再来做一次我们上面的题目:第一题127,正数,没变化;第二题-127,负数,256-127=129,即10000001,上面举例论证过了;第三题,0,不管正负,计算机中可以直接表示出来,所以原码补码都是00000000;第四题:-128,256-128=128,所以补码就是10000000,很特殊,它并没有原码,但是没关系,计算机不认原码;第五题:128,上面我说了这个数溢出了,虽然它在计算机中可以表示,也就是10000000,但是我们人为的将这个值当做了-128,所以在计算机中正数最大能到127,负数最小能到-128,也即补码的区间,即8位bit能表示的最大数量255个数。
再次申明:本文中涉及的所有数字和运算都有一个大前提那就是8位bit下的值

至此,我想表达的都已经说完了,可能大部分人看了会觉得更晕了,其实我在写的时候好几次都把自己绕晕了,所以部分表述不清楚或者不正确的地方请大家及时指出,否则误导了读者就罪过大了。当然一下子没理清楚的也可以多读几次,先抛弃原先的认知,按这我的思路来,应该会好理解很多。

文章开头说了,会有下一篇文章,本来是打算放一起写的,但是发现这些内容都挺无聊的,太长了估计看不下去了,所以决定另起一篇再写,先透露下是关于二进制小数的表示、进制转换和进制的各种运算的,有兴趣的小伙伴可以关注下~~~

    原文作者:coder_yu
    原文地址: https://www.jianshu.com/p/9a674c93f2a5
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞