位运算技巧
写一些常用的位运算的小技巧
其实就是一些简单的常数优化,但可以让程序简单易读,更有逼格
C/C++语言提供的位运算符有:
结果
初步了解以后,就开始学习一些小技巧吧
1、奇偶性判断
没学位运算前,我们这么写
bool check(int value) { if(value % 2 == 0) return false; else return true; } //加以优化 bool check(int value) { return (value % 2 != 0); }
要知道,上面的代码我们使用的是对2取余,如果操作数value是小数的话,还勉强行得通,但是value是一个上百万的大数,那么这就白白浪费了CPU的大量时间,程序的效率和性能就很差。我们知道任何数在计算机储存中都是以二进制储存的,细心的你就会发现在二进制的最小一位有个特点,为0就是偶数,为1就是奇数,按照这个原理我们根本没必要让CPU白白做那么多的工作,只要一步判断就可以了。接下来就让我们看看位运算的精妙之处!
那么我们的目的就是判断最小位是0还是1,可是我们怎么判断呢?我们要用位运算阿里判断,就是与或非。在上面的复习之中我们只说了位运算的计算方法,并没有说其用处。那么在这里我们用到的就是“与”!与运算特有的一个功能就是判断指定位上的值(0或1)。我们来看下面的表格(与运算)。
我们要注意一下这里的 操作数2 ,它只有最低位是1,其余位都是0,这就是关键所在,操作数1是随机值。我们看看结果只会有两种结果:0或1。这个结果就取决于操作数1的最低位,它为1时就为1,为0时就为0.
“那么我要判断的是第二位呢?”
好!那我们就把操作数2改为 00000010 那么结果就只会有 00000000 或 00000010 其结果取决于第二位。
有了这个基础那么我们来看看怎么用位运算判断奇偶性吧:
bool check(int value) { if(value & 0x0001 == 0) return false; else return true; } //加以优化 bool check(int value) { return (value & 1 != 0); } //在简化 #define PARITY(value) (value&1)
总结
使用a%2来判断奇偶性和a & 1是一样的作用,但是a & 1要快好多。
2、 判断n是否是2的正整数冪
所谓2的正整数冪就是指 2,4,8,16,32,64,128,256,512,1024,2018………….等数字,若何判断一个数是否是这样的数呢?我们看看不用位运算的计算方法:
#include <math.h> bool IsPowerOfTwo(int value) { for(int i = 0,l = 8*sizeof(value); i < l ;i++) if(pow(2,i) == value) return true; return false; }
在这个算法中,我们使用了一个循环。其原理非常简单就是一一的对比,但是其中还调用了数学函数库,效率大大降低。接下来我们讲讲怎样用位运算来判断。我们首先要研究一下这些数的特性,请看下表(与运算):
我们发现 n&(n-1) = 0 我们可以 用逻辑非 !(n&(n-1)) = 1 。那是不是这样就可以了呢,你会发现 !(0&(0-1)) = 1 但是 0并不是 2的正整数冪。我们可以用 逻辑与 !(n&(n-1)) && n = 1;请看下面的代码:
bool IsPowerOfTwo(INT value) { if((!(n&(n-1)) ) && n == 1) return true; else return false; } //简化 #define ISPOWEROFTWO(n) ((!(n&(n-1)) ) && n)
3、获得int最大值(不码字了,累死了)
int getMaxInt(){ return (1 << 31) - 1;//2147483647, 由于优先级关系,括号不可省略 }
4、获得int最小值
int getMinInt(){ return 1 << 31;//-2147483648 }
5、获得long的最大值
long getMaxLong(){ return ((unsigned long) - 1) >> 1;//2147483647 }
6、乘2运算
int mulTwo(int n){//计算n*2 return n << 1; }
7、除以2运算
int divTwo(int n){//负奇数的运算不可用 return n >> 1;//除以2 }
8、乘2的m次方
int mulTwoPower(int n,int m){//计算n*(2^m) return n << m; }
9、除以2的m次方
int divTwoPower(int n,int m){//计算n/(2^m) return n >> m; }
10、直接交换两个数
void swap(int *a,int *b){ (*a) ^= (*b) ^= (*a) ^= (*b); }
11、取绝对值
int abs(int n){ return (n ^ (n >> 31)) - (n >> 31); /* n>>31 取得n的符号,若n为正数,n>>31等于0,若n为负数,n>>31等于-1 若n为正数 n^0=0,数不变,若n为负数有n^-1 需要计算n和-1的补码,然后进行异或运算, 结果n变号并且为n的绝对值减1,再减去-1就是绝对值 */ }
12、求两个数的最小值
int min(int x,int y){ return y ^ ((x ^ y) & -(x < y)); /*如果x<y x<y返回1,否则返回0, 与0做与运算结果为0,与-1做与运算结果不变*/ }
13、判断符号是否相同
boolean isSameSign(int x, int y){ //有0的情况例外 return (x ^ y) >= 0; // true 表示 x和y有相同的符号, false表示x,y有相反的符号。 }
14、对2的n次方取余
int quyu(int m,int n){//n为2的次方 return m & (n - 1); /*如果是2的幂,n一定是100... n-1就是1111.... 所以做与运算结果保留m在n范围的非0的位*/ }
15、平均数
int getAverage(int x, int y){ return (x + y) >> 1; }