您的位置:首页 > 理论基础

深入理解计算机系统笔记(一)关于位运算求整数平均值

2015-06-28 09:44 525 查看
《深入理解计算机系统》是一本每一个追求更高境界的程序员应该反复研读的优秀书籍,这是我第二次细读本书,于是对书中每个知识点做了一些更深入的扩展和思考。

在本书第二章--信息的表示和处理--中介绍了二进制的位运算,关于位运算的在应用中的妙招可谓不胜枚举,我会对其中比较精妙的应用写一些自己的理解,与广大朋友一起探讨,共同进步。

今天说一下我对位运算求整数的平均值的理解

int average(int x,int y)
{
return ((x&y) + (x^y)>>1);
}       位运算求平均值的方法在性能上与传统的(X+Y)>> 1 的方法并没有什么优势,其唯一的好处就是可以避免溢出问题。那么为什么能避免溢出呢?

       首先我们知道,整数加法出现溢出,一定是两个数的最高位出现了进位(假设正溢出),超出了该数据类型的bit数导致溢出。传统的(X+Y)>> 1 无法避免这个问题。

然后我们来看位运算的方法:

1、x&y 表示什么?

     它取出了两个数中均为1的bit位x1和y1,同时它也等于(x1+y1)/2的值,为什么呢?很简单,因为1+1的均值就是1,恰巧等于&运算的结果。例如,(0010 + 0010)/2 = 0010;  这样我们就通过x&y运算求得了最终均值的第一部分;

2、(x^y)>>1表示什么?

    右移一位表示除以2这个大家都很清楚,而x^y则表示两个数中bit位一个为0一个为1的位,而巧妙的是,0+1=1正好是异或运算的结果。所以(x^y)>>1就表示了均值的剩下那一部分;

3、为什么可以将均值分成这样的两个部分?

    我想这是理解原理的关键,也是理解二进制的关键。

            我们知道十进制的加法里,比如,302+210,其实本质上它应该写成这样,3*100+0*10+2 和 2*100+1*10,也就是说,对应10的幂的系数不为0,那么这些系数对应的项将对求和结果作出”贡献“;同理,二进制的加法里,假设 100011 + 101101,可以按”贡献“对bit位进行分类,全为1,全为0,有一个1的三种。其中全为0的位对求和没有任何”贡献“,所以我们不予考虑。那么现在就考察两种,全为1和有一个1的bit,我们不妨将原数字写成两个部分,:

100011 = 100001 + 000010,

101101 = 100001 + 001100,

这样我们就将原数分解为两部分,两数均为1的bit位和只有一个1的bit位。对这两部分分别求均值。

100001+100001的均值显然仍为100001,这就是x&y的结果;

000010+001100的均值即(x^y)>>1;

最后将两部分相加即原数的均值;

最后一个问题,为什么这样计算就不会溢出?

会不会溢出主要看+运算,也就是看x&y和(x^y)>>1这两个数的最高位是否有可能都为1,如果有可能,那么就可能溢出,反之亦然。

如果x&y的最高位为1,那么x和y的最高位均为1,那么x^y的最高位肯定是0,根据计算机右移操作补最高的原则,(x^y)>>1的最高位同样也为0,所以两部分相加一定是1+0,不会出现溢出。那么有人会说,第二位如果有进位呢?

我们可以看出,x&y和x^y这两个结果中,所有的bit位都是相反的,即如 101010 和 010101 这样的结果,把后者右移一位,会出现进位到最高位的情况吗?

出现进位的bit一定是后者的1右移一位正好遇到前者的1,那么这时候后者这个1前面的第一个0右移之后一定会遇到前者的0,这样就不会导致进位到最高位的情况。除非后者全为1,如果全为1,那么前者一定全为0,显然相加不会出现溢出。

       所以这样的位运算不会产生溢出。

(本人才疏学浅,最后证明不会溢出的过程,还望大家总结一个数学严密的证明过程)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息