常用位运算函数源码解析

来源:互联网 发布:淘宝详情页跳出率 编辑:程序博客网 时间:2024/04/23 18:21
//计算机中数是按补码形式存储的  //判断该整数是否是2的整数次方 //2的整数次方特点是转换为二进制数,1、只在一个位置上有一个1(且这个位置不是最低位)bool IsPowerOfTwo (unsigned int value)  {      return (value > 0) && ((value & (value - 1)) == 0);  }    bool IsPowerOfTwo (int value)  {      return (value > 0) && ((value & (value - 1)) == 0);  }  
//判断两个数是否异号   //异号则最高位符号不同,进行异或后,最高位一定为1,因此会小于零bool IsOppositeSigns(int value1,int value2)  {      return (value1 ^ value2) < 0;  }  
//不使用分支计算绝对值 //我们知道在我们对一个数进行位运算的时候,是在这个数的补码上进行的,对于补码我们知道,正数的补码是原码,负数的补码为原码除了最高位的符号位,取反,然后加1。把补码转换成原码的时候,正数还是原码,负数时把补码除了符号位取反然后加1(我们可以发现如果这时候连符号位也求反,然后加1,与以前不同的只是少了一个符号位,现在实际上就是这个数的绝对值)。//所以我们可以得到对一个负数求绝对值的表达式为int SignReversal(int a)  {      return ~a + 1;  }//那么由这些知识我们可以很快地得到求一个数的绝对值的表达式://先移位来取符号位,int i = a >> 31;要注意如果a为正数,i等于0,为负数,i等于-1。然后对i进行判断——如果i等于0,直接返回。否之,返回~a+1。完整代码如下:int my_abs(int a)  {      int i = a >> 31;      return i == 0 ? a : (~a + 1);  }   //现在再分析下。对于任何数,与0异或都会保持不变,与-1即0xFFFFFFFF异或就相当于取反。因此,a与i异或后再减i(因为i为0或-1,所以减i即是要么加0要么加1)也可以得到绝对值。所以可以对上面代码优化下://【在内存中,数是以补码形式存储的】 unsigned int GetAbs(int value)  {      unsigned int r;  //CHAR_BIT系统宏定义8,mask为最高位    int const mask = value >> (sizeof(int) * CHAR_BIT - 1);  //与最高位异或,0不变,1取反。       r = (value ^ mask) - mask;      return r;  }  
//求以2为底的结果   unsigned int Log2OfPowerOfTwo (unsigned int powerOfTwo)  {      unsigned int log2 = 0;//(powerOfTwo & 0xAAAAAAAA) != 0;      log2 |= ((powerOfTwo & 0xFFFF0000) != 0) << 4;      log2 |= ((powerOfTwo & 0xFF00FF00) != 0) << 3;      log2 |= ((powerOfTwo & 0xF0F0F0F0) != 0) << 2;      log2 |= ((powerOfTwo & 0xCCCCCCCC) != 0) << 1;    log2 |= ((powerOfTwo & 0xAAAAAAAA) != 0) << 0;     return log2;  }  /*第一步:1 1 1 1 | 1 1 1 1 | 1 1 1 1 | 1 1 1 1 | 0 0 0 0 | 0 0 0 0 | 0 0 0 0 | 0 0 0 0 (1<<4相当于16)第二步:1 1 1 1 | 1 1 1 1 | 0 0 0 0 | 0 0 0 0 | 1 1 1 1 | 1 1 1 1 | 0 0 0 0 | 0 0 0 0 (1<<3相当于8)第三步:1 1 1 1 | 0 0 0 0 | 1 1 1 1 | 0 0 0 0 | 1 1 1 1 | 0 0 0 0 | 1 1 1 1 | 0 0 0 0 (1<<2相当于4)第四步:1 1 0 0 | 1 1 0 0 | 1 1 0 0 | 1 1 0 0 | 1 1 0 0 | 1 1 0 0 | 1 1 0 0 | 1 1 0 0 (1<<1相当于2)第五步:1 0 1 0 | 1 0 1 0 | 1 0 1 0 | 1 0 1 0 | 1 0 1 0 | 1 0 1 0 | 1 0 1 0 | 1 0 1 0 (1<<0相当于1)(最低位为位置0,最高位为位置31)经过以上五步可以计算出到底在那些位置上有1,比如最高位有1,则16+8+4+2+1=31主要看在第几个位置上有1,那么这个数就最接近于几。(例如,2^10=1024,经过上述运算第二步8+第四步2=101025还是101026是11) */int Log2OfPowerOfTwo (int powerOfTwo)  {      unsigned int log2 = (powerOfTwo & 0xAAAAAAAA) != 0;      log2 |= ((powerOfTwo & 0xFFFF0000) != 0) << 4;      log2 |= ((powerOfTwo & 0xFF00FF00) != 0) << 3;      log2 |= ((powerOfTwo & 0xF0F0F0F0) != 0) << 2;      log2 |= ((powerOfTwo & 0xCCCCCCCC) != 0) << 1;      return (int)log2;  }  
/*判断一个数是否为2的幂次方。算法的基本原理是因为正整数的原码和补码是一样的。而这个正整数对应的负整数的补码是这个数的反码+1.正整数的原码与这个数的负整数的补码,如果和这个数相同, 那么 这个数就是2的幂次方。举例来说:128的原码为 0000...10000000.-128的补码为1111..10000000.按位与之后的结果为0000...10000000等于0000...10000000176的原码为 0000...10110000.-176的补码为1111...01010000.按位与之后的结果为0000...00010000不等于0000...10110000。*/public static boolean isPowerOf2(int n) {        return (n & -n) == n;}
//大于等于value的最近的二的次方的数  /*算法基本原理是将这个数的每一位都置为1,然后+1即可。拿10进制来说,计算大于或等于573的最小的10幂次方数就是999+1=1000.当然上述的做法对于如果这个数恰好是你所需要的m次方数,就不大好。所以在实际计算的时候会先减去1,再按位或循环右移1位的结果,最后再+1。如果是32位系统需要循环右移5次,如果是64位系统则需要循环右移6次。循环的次数是如何决定的呢?因为每次迭代都会使1的位数翻倍,2的5次方为32,所以对于32位系统需要循环右移5次。同理对于64位系统需要循环右移6次。这种算法的优点是采用移位操作速度比较快,而且相比其他算法不用考虑溢出问题。举例来说:153的二进制码为10011001计算过程如下:10011001-1=1001100010011000|01001100=1101110011011100|01101110=1111111011111110|11111111=1111111111111111|11111111=1111111111111111|11111111=1111111111111111+1=100000000100000000的10进制数即为256.符合要求。*/unsigned int NextPowerOfTwo(unsigned int value)  {      value--;      value |= value >> 1;      value |= value >> 2;      value |= value >> 4;      value |= value >> 8;      value |= value >> 16;      value++;            //value--;  //这样也可以    //value |= value >> 16;      //value |= value >> 8;      //value |= value >> 4;      //value |= value >> 2;      //value |= value >> 1;      //value++;   return value; }