找到32位int中的第k个非零位

时间:2017-04-20 18:51:02

标签: bit-manipulation opencl bitwise-operators

我正在寻找一种有效的方法来计算32位int中的第k个非零位。

我能想到的最好的是一个串行算法,使用ctz(计数尾随零):

uint test = 0x88;
int p=0;
for (0; p < k; ++p){
  int pos = ctz(test);
  test >>= pos;
}

但我正在寻找更平行的东西。这是一个opencl内核。

编辑: 对于上面的例子,第一个非零位在位置2(基于零) 第二个非零位在第5位。没有其他非零位。

谢谢!

1 个答案:

答案 0 :(得分:2)

这个问题有点不清楚,但我认为你想要的输出是输入整数中第N个设置位的位位置。

唉,我不确定是否有足够的空间来加快这个速度。然而,我们可以尝试将复杂性从线性操作降低到对数操作,这取决于所寻求的典型位数,这可能有助于也可能没有帮助。整数宽度很小且在这里是常数,因此改进的范围是有限的。

这个想法是递归地在输入整数的下半部分执行填充计数。如果目标位超过下半部分的数字,则选择上部并递归上部,否则选择较低部分。

一个好的好处是传统的递归人口计数技巧的部分总数可以在搜索中重复使用。

沿着这些,几乎未经测试的线条:

unsigned int position_of_nth_set_bit(uint_fast32_t input, unsigned int bitno) {
    // Perform the common population-count trick of recursively building up the
    // sub counts as hierarchal bit-fields
    const uint_fast32_t mask1 = UINT32_C(0x55555555);
    const uint_fast32_t mask2 = UINT32_C(0x33333333);
    const uint_fast32_t mask4 = UINT32_C(0x0F0F0F0F);
    const uint_fast32_t mask8 = UINT32_C(0x00FF00FF);

    const uint_fast32_t pop1  = input;
    const uint_fast32_t pop2  = (pop1 >> 1 & mask1) + pop1 & mask1;
    const uint_fast32_t pop4  = (pop2 >> 2 & mask2) + pop2 & mask2;
    const uint_fast32_t pop8  = (pop4 >> 4 & mask4) + pop4 & mask4;
    const uint_fast32_t pop16 = (pop8 >> 8 & mask8) + pop8 & mask8;

    unsigned int bitpos = 0;

    // Recursively check whether our target bit fits into the upper or lower
    // half-space, and shift down according
    unsigned int halfspace;
    if(halfspace = (pop16 & 15), bitno > halfspace) {
        bitno -= halfspace;
        bitpos += 16;
    }
    if(halfspace = (pop8 >> bitpos) & 7, bitno > halfspace) {
        bitno -= halfspace;
        bitpos += 8;
    }
    if(halfspace = (pop4 >> bitpos) & 7, bitno > halfspace) {
        bitno -= halfspace;
        bitpos += 4;
    }
    if(halfspace = (pop2 >> bitpos) & 3, bitno > halfspace) {
        bitno -= halfspace;
        bitpos += 2;
    }
    if(halfspace = (pop1 >> bitpos) & 1, bitno > halfspace)
        bitpos += 1;

    return bitpos;
}