c语言位操作黑科技bithacker


原文链接: c语言位操作黑科技bithacker

http://graphics.stanford.edu/~seander/bithacks.html

1 检测两个数是否异号:

    int x,y;

   bool f= ((x^ y) <0);

2 取最大/小值

int x;  // we want to find the minimum of x and y
int y;   
int r;  // the result goes here 

r = y ^ ((x ^ y) & -(x < y)); // min(x, y)
r = x ^ ((x ^ y) & -(x < y)); // max(x, y)

文章中给出了解释:

以min为例,如果x<y,那么-(x<y)为全1,r = y ^ (x ^ y) & ~0 = y ^ x ^ y = x

如果x>=y 那么,-(x<y)为全0,r = y ^ ((x ^ y) & 0) = y

3计算一个数的2进制右边连续0的个数。

之前看android源码时总能看到一种奇怪的代码:a&-a,不明白是什么意思,这里刚好在这也有,索性仔细研究下:

我打印出了100多个数字的结果(从1开始,0没有啥意义),结果发现一个奇怪的规律:每16个一组会呈现出如下的结果:

1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 64
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 128
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 64
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 256
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 64
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 128
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 64
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 512
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 64
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 128
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 64
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 256
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 64
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 128
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 64
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 1024
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 64
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 128
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 64
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 256
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 64
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 128
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 64
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 32
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 512

看到了吧,前15个都一模一样,最后那个数子不同,如果单看最后一个数字的话,会发现每8个一组,前7个都一样,最后不同,然后观察那最后一个数:规律是:

128 256 128 512 128 256 128  1024

128 256 128 512 128 256 128  2048

128 256 128 512 128 256 128  1024

128 256 128 512 128 256 128  4096

然后再看最后一列数:

1024 2048 1024 4096 1024 2048 1204 8192

1024 2048 1024 4096 1024 2048 1024 16384

看到了吧,依然是8个为一组,可以猜想,最后那列数还会是8个一组。别说这个还真有意思

好了回归正题,v & -v 的作用就是取出右起连续的 0 以及首次出现的 1 。

然后看代码:

unsigned int v; // 32-bit word input to count zero bits on right
unsigned int c = 32; // c will be the number of zero bits on the right
v &= -signed(v);
if (v) c--;
if (v & 0x0000FFFF) c -= 16;
if (v & 0x00FF00FF) c -= 8;
if (v & 0x0F0F0F0F) c -= 4;
if (v & 0x33333333) c -= 2;
if (v & 0x55555555) c -= 1;
我们把v末尾首次出现1的数成为num,即v&-v;
第一个if,如果num不为0,那么至多有31个0,

第2个if,判断后2个字节,如果不为0,那么前2个字节也就没意义了,所以减去16

第3个if,判断最后一个字节,如果不为0,那么倒数第2个字节也没有意义,所以减去8,这里要注意,前后是对称的,因为每一个if都是减半的判断,很有可能第一个if就不执行,因此,在判断后边字节的时候一定要把前边的对称上。

最后两个if就是一半一半的判断最后一个字节,就不用再解释了。

`