一文搞清二进制补码 丨【奔跑吧!JAVA】
引言
点进这个文章说明你对补码的概念也有不清楚的地方,嘻嘻,那就继续看下去吧。
很多同学在学习二进制的时候对于补码的理解不是很清楚,并且也不知道为什么补码是原码先取反然后再加一,这里就来说说我自己的理解,希望能帮到你。
二进制
我们知道计算机内部使用二进制用来表示,准确来说,计算机内部只有二进制,只有0和1,再也没有别的数字了。0和1分别对应低电平和高电平,在磁盘中则代表S极和N极,也可以理解为正 和 反,总之0和1是不一样的两种状态,凡是界限分明的两种状态都可以用来表示0和1。
二进制数包括有符号数和无符号数,这里我们讨论有符号数(因为无符号数也不牵扯补码的问题)。我们知道一个byte占用一个字节,也就是8个bit,在一般的编程课程中一般都会告诉我们,1byte所能表示的数字是2^8= 256个,又因为有符号数中第一位是符号位,第一位是0则代表正数,为1则代表负数,全为0则代表0,所以byte所能表示的范围是-128~127。这里比较精明的同学就会有疑问了,为什么是-128~127而不是-127~128?这将在下面讲解,请继续往下看。
补码的由来
要了解为什么使用补码,那么我们就要知道如果不使用补码会遇到什么样的问题,举个例子大家就明白了,为了方便阅读,我们采用8个bit进行演示,多个字节的情况类似。
比如00000000(8个0)表示0,00000001表示1,01111111表示127,那么按照常规的想法,10000001应该表示-1,10000000应该是-0(也就是0),11111111应该是-127。如下表:
二进制数值 | 对应的十进制数值 | 二进制数值 | 对应的十进制数值 |
00000000 | 0 | 10000000 | -0 |
00000001 | 1 | 10000001 | -1 |
00000010 | 2 | 10000010 | -2 |
01111111 | 127 | 11111111 | -127 |
在计算机中也要遵循传统的数学定理,比如1-1 = 1+(-1) = 0,然而按照刚才的设想,00000001+10000001 = 10000010 = -2 ≠ 0,这违反基本的数学规律,这在一个科学的、自洽的计算机系统中是不允许存在的,这是存在的第一个问题;
第二个问题是,+0和-0占据了两个位置,这样就无法表示256个数了,只能表示255个。
那么我们需要怎么解决这个违反数学规律的问题呢?我们当然难以解决,幸好已经有伟大的计算机科学家找到了完美的解决方案,那就是补码。补码的规则是源码按位取反得到反码,然后再加1,就得到补码。补码与原码互为对称关系,即补码是原码的补码,原码也是补码的补码,这句话有点绕口,多读几遍就理解了。同理,原码与反码也是对称关系,反码是源码的反码,源码是反码的反码。
我们再举例子,00000001(1)取反得到11111110,再加1得到11111111,然后将00000001 + 11111111 = 00000000 = 0!!! 所以11111111即为-1
00000010(2)取反得到11111101,再加1得到11111110,然后将00000010 + 11111110 = 00000000 = 0!!! 所以11111110即为-2
01111111(127)取反得到10000000,再加1得到10000001,然后将 01111111 + 10000001 = 00000000 = 0!!! 所以10000001即为-127
。。。以此类推就可以得到-1~-127所对应的二进制编码,并且满足负数的首位(符号位)是1。
最后还剩一下一个10000000和00000000,00000000对应0,那么只剩下一个-128和10000000对应,那么为什么把10000000分配给-128而不是+128?这是因为10000000 + 00000001(1) = 10000001 = -127 ,所以10000000就等于-127 - 1 = -128。至此为止,8bit的所对应的所有的256个二进制数均与十进制数对应完毕,且完全满足数学规律,这就是我们想要得到的。
关键结论
从上面的例子不难看出,满足数学规律的情况下,
补码所代表的就是原码的相反数,补码所代表的就是原码的相反数,补码所代表的就是原码的相反数!!!
重要的事情说三遍。
可逆性验证
进行补码的逆运算: 11111111(-1) 减1得到 11111110,然后按位取反得到 00000001 (1)
进行补码运算:11111111 (-1)取反得到 00000000,再加1得到 00000001(1)
这里表明了两种运算的方式,都可以得到-1 的相反数 1,两种运算都可以,是等价的,这也就是取补码的两种方式,也就是得到相反数的方式。这里只是举个例子,其他情况下也是符合的,请大家自行验证。
这里只是举8个bit位来说明问题,int表示4个字节,long表示8个字节的情况大家可以自行脑补。
常见数值的换算对比表
下表列出常用的几个数的二进制与十进制的对应关系:
二进制数值 | 对应的十进制数值 | 二进制数值 | 对应的十进制数值 |
00000000 | 0 | 10000000 | -128 |
00000001 | 1 | 11111111 | -1 |
00000010 | 2 | 11111110 | -2 |
00000011 | 3 | 11111101 | -3 |
01000001 | 65 | 10111111 | -65 |
01111110 | 126 | 10000010 | -126 |
01111111 | 127 | 10000001 | -127 |
结语
结尾了,还有一个疑问没有解决,为什么是这么复杂的计算公式,先取反再加1来得到补码?
这是因为取反之后的反码与原码相加会得到11111111(8个1),再加1,就会得到00000000(8个0,也就是0,限定8位,多余的一位会被计算机丢弃),两个数相加为0,这就是相反数的特质,所以依靠这种方式能得到和为0,也就能得到相反数。这样计算之后得到的结果有完美的的可逆性与逻辑性,也满足了数学规律并且没有重复分配,如此精妙绝伦,完美结合了数学特征和计算机的特质,真的佩服老一代计算机科学技术的睿智与深邃的智慧。
为什么这些计算机科学家可以这么有智慧,设计出这么完美的公式,而我等只能瞻仰学习他们的成果?我想这是厚积薄发的结果,有了深厚的知识底蕴,才能遇到事情想到最佳的解决方案,完美解决问题。而我们需要做的,就是不断提高知识水平,夯实底蕴,在不断的磨砺锻炼中提高自己,同时对于细节要多练习达到熟能生巧,保持兴趣与竞争力,等待厚积薄发的机会到来,一举成名世人知。谢谢大家!
本文有点啰嗦,这也是为了照顾零基础小白,如果能帮到你,这将是我莫大的荣幸。如果有问题可以评论加私信来和我讨论,我看到的话会及时回复。本人也是初来乍到,希望能得到各位大佬的指点。谢谢!
【奔跑吧!JAVA】有奖征文火热进行中:https://bbs.huaweicloud.com/blogs/265241
- 点赞
- 收藏
- 关注作者
评论(0)