补码的本质

来源:互联网 发布:nc 发送tcp数据 编辑:程序博客网 时间:2024/06/01 18:19

转载自:http://blog.csdn.net/keyinglee/article/details/8298680


RT 这篇文章的由来还有一个背景故事。希望大家能慢慢读完,或许你能有所感悟。本文是上半部分。

在我本周四的一堂网络课上,一名旁听的老教授A和任课老师B为了一个“传播介质”的问题争得面红耳赤。B在课上说了一句:传输介质无线电波。A课后郑重的反驳说:没有你这种说法,无线电波的传输介质是以太,它自己是信号,是信息的载体,根本不是什么介质。其实明白人都知道,两个人的对话不再一个层次。但是A的发言还是让在座的学生感慨万千,特别是我自己。在A离开教室的时候,大家不约而同的鼓掌。说实话,上了这么多年课,上一次给老师由衷鼓掌都过去几年了。

现在的老师讲的东西大多是书上的内容,有些陈旧,有些缺乏考量。很难让学生得到启发。而且讲课大多泛泛而谈,不着本质。从A的话语中,可以感受到老一代教师那种对问题本质和理论的探究。现在的学校值得反思。

扯得有点远,今天之所以要谈补码,是因为A在课堂上问了我一个问题:计算机原理中的为什么要有补码一说,什么是补码,这个问题即使在IT工作了很多年的人也未必能回答清楚。

我知道这个问题有陷阱。我的回答是:书上说,取反加一。A说:这就是错的,现在的教科书95%都是错误连篇。

A的话让我很受启发,所以我今天打算将补码的概念从来源到发展到为什么我们会有错误的认识,统统的理清。由于我的知识有限,我的见解就抛砖引玉,不当的地方欢迎跟帖。

1. 什么是补码?

印象中,除了补码,视乎还有原码,反码,有符号码,无符号码等等乱起八糟一堆。在我的眼里,说的偏激一点,这个世界只有补码,或许有人不同意,但听我慢慢道来。

介绍补码之前,先给个概念“模”。简单说,模是一个计量器的计数范围。具体的概念大家可以去google。举个简单的例子,时钟就是一个可以支持模运算的计量器。比如:现在是12点,如果想要时间变为1点,有至少两个方案:一个是顺时针转1个单位,另一个是逆时针转11个单位。我们来整理一下可以得到这样一个近似于计算机的模型:有一个计算器,技术范围0-11,共十二个单位。其模为12。相信这个大家可以理解。这个是我们进入补码殿堂的第一步。

A教授说万物都是有理论的。那我们就从起源分析一下,补码怎么来的?还以时钟为例(顺时针为加)

12+1=13=12+1=1点(这里第二个12+1表示,在支持模运算的空间中存在溢出,相当于加上12(成为模)的整数倍和原值相等)

12-11=1点 (提醒一下,上面的1和这里的-11彼此可以看做补码)

这个很好理解。这里我们想想计算机领域的一句话:计算机只能做加法,减法要转换成加法计算。这是搞数字逻辑的一句经典法则。那问题是减法怎么做?

到这里,我们已经发现了我们为什么要引进补码这个概念,是为了能够方便的做减法。

补码的引入能够让我们把二进制中的加法和减法统一起来,用加法器运算。

2.如何计算补码?

如果你的回答还是像我的一样:取反加一。那真的需要好好读读这篇文章。记住一句话:对于真正的补码(指的是不仅仅存在于计算机领域的补码),取反加一是教科书一直默许的狗屁。如果你认为我是错的,那么请你用取反加一给我算算-128的补码?(ps:应为1000 0000)

补码的计算法则只有一个:如果是正数(这里包括正0),补码就是它本身;如果是负数,那么补码就是模减去这个数的绝对值。

验证一下:(我们知道X-Y=X+Y的补码)

128-1=128+(-1的补码)=128+(128-1)=128+127=127 (看了上面的文章应该了解为啥128+127=127了吧,128是溢出,计算机中溢出就等于nothing了)

所以-1的补码=128-1 即我们的补码计算法则是正确的。你还可以接着验证。

在下一篇文章,会接着本文回答几个疑问。

a.在计算机中数值都用补码存储的原理(百度百科补码,有这一说法)

b.既然“取反加一”是错误的(严格讲 不是错误 而是没能从本质上说明补码的计算原理),还是被广为传播的原因。


上面文章提出了两个问题。这里我来用自己的理解回答一下。同样,欢迎讨论。

1.用补码存储数值的原理

首先,上篇文章中提到,正数的补码是其本身。这里我来说明一下原理。ps:A教授说万事都是有需求才产生的。

仍然以亲切的时钟先生为例:我们说12-1=12+(-1)的补码=12+(12-1)=12+11=11 [记这个式子为M],这样做是因为补码的引入让减法变成了加法运算。这里,我们试着使用负数计算补码的法则来计算正数的补码(要做一点修改):12+1=12 - 1的补码=12 -(12-1)=12-11=1[记这个式子为N]。之所以N中是减去1的补码,而M中是加上1的补码,是因为在做加减法两种运算时,补码起的作用有点不同。举个例子:

补码我们不妨认为是完成一个事情有2个方法,X和Y。这里的X和Y互补。这点和上一篇文章中的原理相似。

现在的任务是我们在圆环上有两个点P1,P2。两个点的圆心角为90°,要从P1到P2。所以X就是等价于顺时针走90°,Y等价于逆时针走270°。所以当用模360来计算时,X等价于360+90,Y等价于360-270。这里的加法和减法的原理就和M,N的原理相同。

所以通过上面M和N的论证,我们统一了正数和负数的补码计算原则。即均为模减去这个数本身的绝对值,前提是考虑了加法和减法的因素。

但是计算机是愚蠢的,它不知道如何决策加法和减法的因素。所以科学家们想了一个妙计,让计算机只需要机械的做加法就行:为了得到一次数学运算的正确结果,我们在计算机中用补码的形式存储这个数。

这样做的依据可从M和N式的推导得出:先看N式,忽略中间的过程左右两边12+1=1在模空间是成立的。再看M式,我们调整一下式子的顺序,12+(-1的补码)=11=12-1,两个式子都得到了正确的结果。所以我们可以认为,1的补码就是1。这样我们就可以统一这两个式子为:12+(数的补码)=结果。

到这里,关于为啥要用补码形式存储就已经浮出水面了。

如果还觉得有点混乱,那我来用“科学的口吻”倒叙总结一下:

补码计算定律:一个数不论正负,在参与运算时它的补码为±(模-它的绝对值)

补码计算定理:一个数,如果为正数,它的补码为它本身;如果为负数,它的补码为(模-它的绝对值) ps:定理是由定律推导的。这样的区分是的计算机计算时不用判断符号。还不明白的再从上读

讲了这么多,我觉得已经比较清晰了。你输入的一个数在存入计算机的时候就已经是以补码的形式存在,可以称其为真值。比如-1存的是11,所以计算12-1实际上计算机的操作是12+11=11. Clear? ps:必须有一个认识,补码是我们试图解释计算机正确运算结果的一个概念,而存储的真值就是一串01。

这里补充一下,计算机存储的时候没有符号的概念,符号的产生是由于软件(比如编译器)的处理和人的理解需要。

2.取反加一的由来

百度百科补码会有相关解释。这里我再理一下取反加一和求补码的联系。

假设一个整数a,那-a就是负数,由K0K1...K(n-1)共n位二进制组成,n位二进制,最高位为符号位,因此表示的数值范围-2^(n-1)至2^(n-1) -1,所以模为2^(n-1)。任何一个数都可以表示为2^(n-1)-a=2^(n-1)-a,可以看出2^(n-1)即使模,a是正数即可以看做绝对值。所以等式左边的含义就是求一个数的补码。下面来计算右边。

而根据二进制转十进制数的方法,我们可以把a表示为:a=k0*2^0+k1*2^1+k2*2^2+……+k(n-2)*2^(n-2),第(n-1)位为符号位不计算在内。 2^(n-1)=1+2^0+2^1+2^2+……+2^(n-2),这里k0,k1,k2,k(n-2)是1或者0,所以右边2^(n-1)-a=(1-k(n-2))*2^(n-2)+(1-k(n-3))*2^(n-3)+……+(1-k2)*2^2+(1-k1)*2^1+(1-k0)*2^0+1,而这步转化正是取反再加1的规则的代数原理所在。因为这里k0,k1,k2,k3……不是0就是1,所以1-k0,1-k1,1-k2的运算就是二进制下的取反,而为什么要加1,追溯起来就是2^(n-1)的二项展开式最后还有一项1的缘故。

所以,取反加一只是求补码在计算机中而且是二进制的计算机中才引申出的一种便于计算的解法。加入不是二进制那么取反就无从谈起了。谁敢说这个世界就一定不会有三进制?

关于补码,通过这两篇文章我把自己观点阐述了一下。理解补码一定要从补码的由来和计算机组成的原理的角度出发。否则就会绕在各种码的纠结中。一贯的,如果有不同意见,欢迎指正。



原创粉丝点击