① 第一次按1-bit划分,将成对相邻的两个1-bit相加即为每2-bit中’1’的个数。由于两位中’1’的个数最多为2所以2-bit可以表示;
② 按2-bit划分,相邻两个2-bit相加即为每4-bit中’1’的个数。同样不会溢出。
③ 按4-bit划分…
④ 最后就能得到整个二进制中’1’的个数。
// 通过a^b得到没有进位的结果,通过a&b得到进位,递归相加 intgetSum(int a, int b){ return b==0? a:getSum(a^b, (a&b)<<1); //be careful about the terminating condition; }
找到最接近给定数字的2n
1 2 3 4 5 6 7 8 9 10
//实际就是定位数字最大端1的位置,将所有位置1后加一右移,即得到答案 longlargest_power(long N){ //changing all right side bits to 1. N = N | (N>>1); N = N | (N>>2); N = N | (N>>4); N = N | (N>>8); N = N | (N>>16); return (N+1)>>1; }
判断数字是否是2n
1 2 3 4 5 6
//如果X只有一个1,那么-1后,除了1的那一位,其他都会变为1,这样两个数字求与,应该得到0. boolisPowerOfTwo(int x) { // x will check if x == 0 and !(x & (x - 1)) will check if x is a power of 2 or not return (x && !(x & (x - 1))); }
intBitLength2(unsignedint n) { if(n == 0) return0 ; // pow of 2, 2^0 - 2 ^32 unsignedint powof2[33] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967295, } ; int left = 0 ; int right = 32 ; while (left <= right) { int mid = (left + right) / 2 ; if (powof2[mid] <= n) { if (powof2[mid + 1] > n) return mid + 1; elseif(powof2[mid] == n) // for max 32bit unsigned int 4294967295 return mid ; else// powof2[mid] < n, search right part left = mid + 1 ; } else// powof2[mid] > n, search left part right = mid - 1 ; } return-1 ; // not found }
二分累加
1 2 3 4 5 6 7 8 9 10 11 12 13 14
intBitLength(unsignedint n){ if (n>2147483647) {return32;} n<<=1; int cnt=0; int bsheet[]={16,8,4,2,1}; for (int i=0;i<5;i++){ int tmp=n>>bsheet[i]; if (tmp){ n=tmp; cnt+=bsheet[i]; } } return cnt; }
无分支版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
intBitLength(unsignedint N){ //把数字从最高位起全用1填充. n = n | (n>>1); n = n | (n>>2); n = n | (n>>4); n = n | (n>>8); n = n | (n>>16); //统计1的个数 n=(n & 0x55555555) + ((n>>1) & 0x55555555); n=(n & 0x33333333) + ((n>>2) & 0x33333333); n=(n & 0x0f0f0f0f) + ((n>>4) & 0x0f0f0f0f); n=(n & 0x00ff00ff) + ((n>>8) & 0x00ff00ff); n=(n & 0x0000ffff) + ((n>>16) & 0x0000ffff); return n; }
位扫描指令
代码的其他片段都很好理解,重点是一条汇编指令:bsr 。这条汇编指令的意思是 Bit Scan Reverse (反向位扫描指令)。这条指令接受一个参数,会从高位扫描至低位,遇到一停止,将目标寄存器设置为该位的下标。所以,这应该是无论如何最快的取二进制最高位的方法了, 性能直逼空函数 。