int SWAR(unsigned int i)
{
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}
我看过这段代码,它计算32位整数中等于1
的位数,我注意到它的性能比__builtin_popcount
更好,但我不能理解它的工作方式。
有没有人能详细解释一下这段代码是如何工作的?
发布于 2015-04-14 15:53:21
我更喜欢这个,它更容易理解。
x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x & 0x0f0f0f0f) + ((x >> 4) & 0x0f0f0f0f);
x = (x & 0x00ff00ff) + ((x >> 8) & 0x00ff00ff);
x = (x & 0x0000ffff) + ((x >> 16) &0x0000ffff);
发布于 2017-02-02 00:28:41
这是对Ilamari's answer的评论。我把它作为一个答案,因为格式问题:
第1行:
i = i - ((i >> 1) & 0x55555555); // (1)
此行是从此easier to understand line派生的
i = (i & 0x55555555) + ((i >> 1) & 0x55555555); // (2)
如果我们调用
i = input value
j0 = i & 0x55555555
j1 = (i >> 1) & 0x55555555
k = output value
我们可以重写(1)和(2)以使解释更清楚:
k = i - j1; // (3)
k = j0 + j1; // (4)
我们想证明(3)可以由(4)导出。
i
可以写入为其偶数位和奇数位的相加(将最低位计数为位1=奇数):
i = iodd + ieven =
= (i & 0x55555555) + (i & 0xAAAAAAAA) =
= (i & modd) + (i & meven)
由于meven
掩码清除了i
的最后一位,因此可以这样写最后一个相等:
i = (i & modd) + ((i >> 1) & modd) << 1 =
= j0 + 2*j1
这就是:
j0 = i - 2*j1 (5)
最后,将(5)替换为(4),我们得到(3):
k = j0 + j1 = i - 2*j1 + j1 = i - j1
发布于 2022-01-17 16:52:58
这是对yeer's answer的解释
int SWAR(unsigned int i) {
i = (i & 0x55555555) + ((i >> 1) & 0x55555555); // A
i = (i & 0x33333333) + ((i >> 2) & 0x33333333); // B
i = (i & 0x0f0f0f0f) + ((i >> 4) & 0x0f0f0f0f); // C
i = (i & 0x00ff00ff) + ((i >> 8) & 0x00ff00ff); // D
i = (i & 0x0000ffff) + ((i >> 16) &0x0000ffff); // E
return i;
}
让我们使用A行作为我的解释的基础。
i = (i & 0x55555555) + ((i >> 1) & 0x55555555)
让我们将上面的表达式重命名如下:
i = (i & mask) + ((i >> 1) & mask)
= A1 + A2
首先,不要把i
看作32位,而是一个由16组组成的数组,每组2位。A1
是大小为16的计数数组,每组包含i
中相应组最右边的1
的计数
i = yx yx yx yx yx yx yx yx yx yx yx yx yx yx yx yx
mask = 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
i & mask = 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x
类似地,A2
为i
中的每个组“计数”最左边的位。请注意,我可以将A2 = (i >> 1) & mask
重写为A2 = (i & mask2) >> 1
i = yx yx yx yx yx yx yx yx yx yx yx yx yx yx yx yx
mask2 = 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
(i & mask2) = y0 y0 y0 y0 y0 y0 y0 y0 y0 y0 y0 y0 y0 y0 y0 y0
(i & mask2) >> 1 = 0y 0y 0y 0y 0y 0y 0y 0y 0y 0y 0y 0y 0y 0y 0y 0y
(请注意,mask2 = 0xaaaaaaaa
__)
因此,A1 + A2
将A1
数组和A2
数组的计数相加,得到16个组的数组,每个组现在包含每个组中的位数。
移到行B,我们可以重命名该行,如下所示:
i = (i & 0x33333333) + ((i >> 2) & 0x33333333)
= (i & mask) + ((i >> 2) & mask)
= B1 + B2
B1 + B2
遵循与之前的A1 + A2
相同的“形式”。i
不再是16个2位的组,而是8个4位的组。因此,与前面类似,B1 + B2
将B1
和B2
的计数相加,其中B1
是组右侧的1
计数,B2
是组左侧的计数。因此,B1 + B2
是每个组中的比特计数。
C到E行现在变得更容易理解了:
int SWAR(unsigned int i) {
// A: 16 groups of 2 bits, each group contains number of 1s in that group.
i = (i & 0x55555555) + ((i >> 1) & 0x55555555);
// B: 8 groups of 4 bits, each group contains number of 1s in that group.
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
// C: 4 groups of 8 bits, each group contains number of 1s in that group.
i = (i & 0x0f0f0f0f) + ((i >> 4) & 0x0f0f0f0f);
// D: 2 groups of 16 bits, each group contains number of 1s in that group.
i = (i & 0x00ff00ff) + ((i >> 8) & 0x00ff00ff);
// E: 1 group of 32 bits, containing the number of 1s in that group.
i = (i & 0x0000ffff) + ((i >> 16) &0x0000ffff);
return i;
}
https://stackoverflow.com/questions/22081738
复制相似问题