【问题标题】:Finding trailing 0s in a binary number在二进制数中查找尾随 0
【发布时间】:2011-12-10 08:30:32
【问题描述】:

如何在二进制数中查找尾随 0 的数量?基于在二进制数中查找 1 的 K&R bitcount 示例,我对其进行了一些修改以找到尾随 0。

int bitcount(unsigned x)
{
  int b;
  for(b=0;x!=0;x>>=1)
      {
        if(x&01)
          break;
        else
          b++;
      }

我想回顾一下这个方法。

【问题讨论】:

标签: c binary


【解决方案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;

【讨论】:

  • 它是如何工作的?很难理解它是如何产生的。
  • 这是位级别的二分法/二分搜索。
【解决方案2】:

在 X86 平台上的 GCC 上,您可以使用 __builtin_ctz(no) 在 X86 的 Microsoft 编译器上,您可以使用 _BitScanForward

它们都发出 bsf 指令

【讨论】:

    【解决方案3】:

    另一种方法(我很惊讶这里没有提到)是构建一个包含 256 个整数的表,其中数组中的每个元素都是该索引的最低 1 位。然后,对于整数中的每个字节,您在表中查找。

    类似这样的东西(我没有花时间调整这个,这只是为了大致说明这个想法):

    int bitcount(unsigned x)
    {
       static const unsigned char table[256] = { /* TODO: populate with constants */ };
    
       for (int i=0; i<sizeof(x); ++i, x >>= 8)
       {
          unsigned char r = table[x & 0xff];
    
          if (r)
             return r + i*8;    // Found a 1...
       }
    
       // All zeroes...
       return sizeof(x)*8;
    }
    

    使用一些表驱动方法来解决此类问题的想法是,if 语句在分支预测方面会花费您一些成本,因此您应该致力于减少它们。它还减少了位移的数量。您的方法执行if 语句和每位移位,而此方法每字节执行一次。 (希望优化器可以展开 for 循环,而不是为此发出比较/跳转。)其他一些答案的 if 语句比这更少,但表格方法简单易懂。当然,您应该以实际测量为指导,看看这些是否重要。

    【讨论】:

      【解决方案4】:

      我认为您的方法有效(尽管您可能想使用unsigned int)。每次检查最后一位,如果为零,则丢弃它,增加尾随零位的数量。

      我认为对于尾随零,您不需要循环。

      考虑以下几点:

      • 如果减去 1,数字(当然是二进制表示)会发生什么情况?哪些数字改变,哪些保持不变?
      • 如何将原始数字和递减版本结合起来,以便只剩下代表尾随零的位?

      如果您正确应用上述步骤,您可以在 O(lg n) 步骤中找到设置的最高位(如果您对如何操作感兴趣,请查看 here)。

      【讨论】:

        【解决方案5】:

        应该是:

        int bitcount(unsigned char x)
        {
          int b;
          for(b=0; b<7; x>>=1)
          {
            if(x&1)
              break;
            else
              b++;
          }
          return b;
        }
        

        甚至

        int bitcount(unsigned char x)
        {
          int b;
          for(b=0; b<7 && !(x&1); x>>=1) b++;
          return b;
        }
        

        甚至(耶!)

        int bitcount(unsigned char x)
        {
          int b;
          for(b=0; b<7 && !(x&1); b++) x>>=1;
          return b;
        }
        

        或者...

        啊,无论如何,there are 100500 millions methods of doing this。使用任何你需要或喜欢的东西。

        【讨论】:

        • 链接仅供参考,用于位计数。但你明白了。
        • 这些代码不保存时x&gt;&gt;1有什么用?
        • 嗯,从尝试执行此类任务的人那里听到这个问题很奇怪。这称为位移:cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/bitshift.html
        • “移位不会改变值”,在您提到的链接中说。 x&gt;&gt;1; 不将结果保存在变量中。这就像写i+1; !!!
        【解决方案6】:

        我们可以使用位操作轻松获得它,我们不需要遍历所有位。伪代码:

        int bitcount(unsigned x) {
            int xor = x ^ (x-1); // this will have (1 + #trailing 0s) trailing 1s
            return log(i & xor); // i & xor will have only one bit 1 and its log should give the exact number of zeroes
        }
        

        【讨论】:

        • i 是什么?
        【解决方案7】:
        int countTrailZero(unsigned x) {
            if (x == 0) return DEFAULT_VALUE_YOU_NEED;
            return log2 (x & -x);  
        }
        

        解释:

        x & -x 返回设置为 1 的最右边的位数。

        例如6 -> "0000,0110", (6 & -6) -> "0000,0010"

        你可以用两个补码来减去这个: x = "a1b",其中 b 表示所有尾随零。 那么

        -x = !(x) + 1 = !(a1b) + 1 = (!a)0(!b) + 1 = (!a)0(1...1) + 1 = (!a)1(0...0) = (!a)1b 
        

        所以

        x &amp; (-x) = (a1b) &amp; (!a)1b = (0...0)1(0...0)

        您只需执行 log2 即可获得尾随零的数量。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-09-25
          • 2020-02-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-03-18
          • 1970-01-01
          相关资源
          最近更新 更多