由于计算机的硬件决定,任何存储于计算机中的数据,其本质都是以二进制码存储。
根据冯·诺依曼提出的经典计算机体系结构框架,一台计算机由运算器、控制器、存储器、输入和输出设备组成。其中运算器只有加法运算器,没有减法运算器(据说一开始是有的,后来由于减法运算器硬件开销太大,被废了)。
所以计算机中没办法直接做减法的,它的减法是通过加法实现的。现实世界中所有的减法也可以当成加法的,减去一个数可以看作加上这个数的相反数,但前提是要先有负数的概念,这就是为什么不得不引入一个符号位。符号位在内存中存放的最左边一位,如果该位位0,则说明该数为正;若为1,则说明该数为负。
而且从硬件的角度上看,只有正数加负数才算减法,正数与正数相加,负数与负数相加,其实都可以通过加法器直接相加。
原码、反码、补码的产生过程就是为了解决计算机做减法和引入符号位的问题。
一、原码
原码:是最简单的机器数表示法,用最高位表示符号位,其他位存放该数的二进制的绝对值。
以带符号位的四位二进制数为例:1010,最高位为1表示这是一个负数,其它三位010,即0*2^2+1*2^1+0*2^0=2,所以1010表示十进制数-2。
部分正负数的二进制原码表示原码的表示法很简单,虽然出现了+0和-0,但是直观易懂。于是开始运算——
0001+0010=0011,1+2=3;
0000+1000=1000,+0+(-0)=-0;
0001+1001=1010,1+(-1)=-2。
于是可以看到其实正数之间的加法通常是不会出错的,因为它就是一个很简单的二进制加法,而正数与负数相加,或负数与负数相加,就要引起莫名其妙的结果,这都是符号位引起的。0分为+0和-0也是因它而起。
原码的特点:
1. 原码表示直观、易懂,与真值转换容易。
2. 原码中0有两种不同的表示形式,给使用带来了不便。
通常0的原码用+0表示,若在计算过程中出现了-0,则需要用硬件将-0变成+0。
3. 原码表示加减运算复杂。
利用原码进行两数相加运算时,首先要判别两数符号,若同号则做加法,若异号则做减法。在利用原码进行两数相减运算时,不仅要判别两数符号,使得同号相减,异号相加;还要判别两数绝对值的大小,用绝对值大的数减去绝对值小的数,取绝对值大的数的符号为结果的符号。可见,原码表示不便于实现加减运算。
二、反码
原码最大的问题就在于一个数加上它的相反数不等于0,于是反码的设计思想就是冲着解决这一点,既然一个负数是一个正数的相反数,那干脆用一个正数按位取反来表示负数。
反码:正数的反码还是等于原码;负数的反码就是它的原码除符号位外,按位取反。
以带符号位的四位二进制数为例:3是正数,反码与原码相同,则可以表示为0011;-3的原码是1011,符号位保持不变,低三位按位取反,所以-3的反码为1100。
部分正负数的二进制反码表示再试着用反码的方式解决一下原码的问题——
0001+1110=1111,1+(-1)=-0;
1110+1100=1010,(-1)+(-3)=-5。
互为相反数相加等于0,虽然的到的结果是1111也就是-0。但是两个负数相加的出错了。
反码的特点:
反码表示在计算机中往往作为数码变换的中间环节。
三、补码
补码:正数的补码等于它的原码;负数的补码等于反码+1(这只是一种算补码的方式,多数书对于补码就是这句话)。
其实负数的补码等于反码+1只是补码的求法,而不是补码的定义,很多人以为求补码就要先求反码,其实并不是,那些计算机学家并不会心血来潮的把反码+1就定义为补码,只不过补码正好就等于反码+1而已。
如果有兴趣了解补码的严格说法,建议可以看一下《计算机组成原理》,它会用“模”和“同余”的概念,严谨地解释补码。
四、补码的思想
补码的思想,第一次见可能会觉得很绕,但是如果肯停下来仔细想想,绝对会觉得非常美妙。
补码的思想其实就是来自于生活,只是我们没注意到而已,如时钟、经纬度、《易经》里的八卦等。补码的思想其实就类似于生活中的时钟。
如果说现在时针现在停在10点钟,那么什么时候会停在八点钟呢?
简单,过去隔两个小时的时候是八点钟,未来过十个小时的时候也是八点钟。
也就是说时间倒拨2小时,或正拨10小时都是八点钟。+
也就是10-2=8,而且10+10=8。
这个时候满12,说明时针在走第二圈,又走了8小时,所以时针正好又停在八点钟。
所以12在时钟运算中,称之为模,超过了12就会重新从1开始算了。
也就是说,10-2和10+10从另一个角度来看是等效的,它都使时针指向了八点钟。
既然是等效的,那么在时钟运算中,减去一个数,其实就相当于加上另外一个数(这个数与减数相加正好等于12,也称为同余数),这就是补码所谓运算思想的生活例子。
在这里,再次强调原码、反码、补码的引入是为了解决做减法的问题。在原码、反码表示法中,我们把减法化为加法的思维是减去一个数等于加上这个数的相反数,结果发现引入符号位,却因为符号位造成了各种意想不到的问题。
但是从上面的例子中,可以看到其实减去一个数,对于数值有限制、有溢出的运算(模运算)来说,其实也相当于加上这个数的同余数。
也就是说,不引入负数的概念,就可以把减法当成加法来算。
在这次的课堂测试当中,我遇到了很多问题,其中最难度比较大的问题是括号的添加,要整理好程序运行的顺序,构思好程序的结构,让括号出现在应该出现的位置。