前言
在平时的编程中,当进行整数运算时,经常会遇到一些奇怪的结果,比如两个正数相加出现了负数,两个负数相加出现了正数,这些都是因为数值表示的有限性导致的.来看一个案例:
int a = 0x7FFFFFFF;
int b = 0x7FFFFFFF;
Console.WriteLine(a);
Console.WriteLine(b);
Console.WriteLine(a+b);
我这是C#的代码,我告诉你输出结果吧:-2
程序中的a和b都是很大的正整数,结果它们相加会得到一个负数.
再看看用C语言写的代码:
#include <stdio.h>
int main(){
int a = 0x7FFFFFFF;
int b = 0x7FFFFFFF;
printf("%d\n",a);
printf("%d\n",b);
printf("%d\n",a+b);
}
输出结果也是-2
这是咋个回事呢?下面跟随你单哥咱们一起探个究竟!
无符号加法
记得小时候杂学的加法把?我们都是使用的图画表示,就是上面是加数下面是加数,左边一个加号,逢十进一,然后下面一条横线,横线下面是结果.想起来了吧
对于二进制整数来说,其实是可以用这种最原始的方式去计算,由此也可以认识到,二进制整数的加法也是非常简单的.只不过与我们平时的十进制算法有一个最大的区别,那就是我们在计算机当中进行计算时,结果的位数都是有限制的.因此我们计算完成后,可能需要对结果进行截断操作.
前面一节咱们刚说了有关阶段的内容,那么明显这里就这样用上了.这里使用的时候有一个前提,我们可以假设是进行w位的二进制运算,那么在运算之后的实际结果也一定是w位的.这里有两种情况,一种结果依然是w位的,也就是w+1位为0.第二种则是到达了w+1位,这个时候我们需要将结果截断到w位.(明白不?就是3+3和9+9的案例,一个是6,6是一位,一个是18,18是两位,还不明白,我建议你从事别的专业吧,计算机可能不太适合你...)
第一种情况属于正常的加法运算,对于第二种来说,我们根据上一张的结论,假设完整的结果为sum,则实际的结果最终为sum mod 2w。
在书中给出了一个公式,它得到了这个结果的一个前提是两个操作数都满足小于2w,它与我们上面取模的结果其实是一样的。
再稍微解释一下,对于第一种情况,x+y < 2w,则sum mod 2w是与sum一致的。对于第二种来说,当 2w =< x+y < 2w+1,对 x + y 进行2w的取模运算,与 x + y - 2w是等价的。
无符号的非
无符号的加法会形成一个阿贝尔群,这意味着无符号的加法满足一些特性.比如说可交换,可结合等等.在这个群中,单位元为0,那么每一个群中的元素,就是每一个无符号数u,都会拥有一个逆元u-1,满足 u+ u-1 = 0。这个结论的来源是,对于w位上的无符号运算来讲,倘若两个无符号数的加法运算结果为2w,也就是1后面跟w个0,此时截断之后的结果则为0。
从以上的简单分析,我们可以很容易的得到一个无符号数的逆元满足以下公式(公式中的左边就是我写的u-1,由于图中的符号在博文中不好编辑,所以我以u-1替代)。
补码加法
对于不骂的加法来讲,我们会建立在无符号假发的基础上来进行,这么做的一个重要前提是,他们的位表示都是一样的.
其实补码加法就是先按照无符号加法进行运算,而后再进行无符号和有符号的的转换.因此我们根据上面的结论可得,对于两个补码编码的有符号数来说,他们进行加法运算的最终结果为,假设实际的无符号结果为sum,那么最终的实际结果为:
U2Tw(sum mod 2w)。
上面这个结论看起来很简单,但是实际上它的运算结果还是比较复杂的,树中给出了四种情况的分析,采用数学推导和证明的方式来说明.
与无符号加法不同的是,这里会出现三种结果,一种是正常的结果,一种是正溢出,一种数负溢出.
对于正溢出的时候,我们的结果与无符号数类似,取模之后等价于减去2的w次方.而当负溢出的时候,则刚好相反,取模之后的结果等价于加上2的w次方.更直观的,由于我们最终科表示的补码范围在-2w-1(包含)到2w-1之间,所以我们总是要试图将最终的实际结果报纸在这个范围之内.于是我们可以直观的得到下面的结果
补码的非
对于补码来说,它同样的与无符号有一样的特性,也就是对于任意的一个w位的补码t来说,他都有唯一的逆元t的-1次方,是的t+t的-1次方=0.
一个w位的补码数范围在-2的w-1次方(包含)到2的w-1次方之间,直观的可以看出,对于不等于-2的w-1次方的补码数x来说,他的逆元就是-x.而对于-2的w-1次方来说,他的二进制位表示为1后面跟着w-1个0,我们需要找到一个数与其相加之和结果为0.
这种时候我们需要考虑的是,如果是-x,也就是2的w-1次方,则它的位表示需要w+1位,是不存在.因此我们需要考虑溢出的情况,对照上面的公式2.14,负溢出的时候需要加上2的w次方,因此-2的w-1次方的逆元就是-2的w次方+2的w-1次方=-2的w-1次方,也就是他本身.
综上,最终我们可以得到补码的逆元满足以下公式(这里与上面一样,公式左边是我们所说的逆元t的-1次方).
二进制整数的减法
减法运算器是是可以由加法运算替代的,我们上面已经介绍过了无符号和补码的非,其实很多CPU是没有减法运算器的,他们都是将减数进行逆运算以后送入加法器,然后进行加法运行,这样得出来的结果就是减法运算最终的结果.
比如我们考虑一种简单的情况,当w=4时的无符号减法运算,对于5-4这个减法运算来说,我们可以由5+4的-1次方(其中4的-1次方是4的逆元的意思,不是四分之一的意思)来替代这个减法运算.
为了更加直观,咱们一起来算一下,首先4的逆元根据上面的公式可以得到4的-1次方=2的4次方-4=12.呢么我们现在需要对5和12进行加法运算,他们的位分别表示为0101和1100,结果为10001,就是十进制17的位表示.不过由于我们的w=4,因此截断之后结果为0001,就是十进制的1.最终结果得到5-4=1.
对于5-4来说,是考虑的结果为正的情况.或许有人认为会对结果为负或者说无符号数移除的情况下有疑问,因此咱在这里对这种情况也做了一个简单的介绍.我们考虑一个简单的计算0-1,我们可以得到1的-1次方=2的4次方-1=15.此时对0和15进行加法计算,他们的位表示分别为0000和1111,结果为1111.
看到这里估计有人会有疑问了,这是怎么回事,0-1=15?
当然不是,,,这个结果其实是正确的.考虑使用补码编码来解析1111这个位表示,它代表的值就是-1.15是1111这个位表示在无符号编码情况下的解析结果.
因此这里有一个公式,就是对于两个整数x和y来说,x-y=x+y的-1次方,这个公式代表的意义是位表示,而不是实际的数值.
小结
主要讲解了二进制整数的加法运算,还有剪发的简单介绍.
版权声明:本文为博主原创文章,未经博主允许不得转载。