隔离字符中的1到1的字符串

时间:2013-03-24 21:47:38

标签: c++ bit-manipulation

我需要提出一个函数,它接受一个char和一个设置位的索引,并隔离一个包含该位的1的字符串。

char isolate(unsigned char arg, int i);

例如:

隔离(221,2)将返回28(11011101>>>> 00011100)

isolate(221,6)将返回192(11011101>>>> 1100000)

查找表似乎是一个笨拙的解决方案,因为它需要~256 * 8 = 2048个条目。

我正在考虑检查索引左侧和右侧的每个位:

char isolate(char arg, int i)
{
   char result=0;
   char mask = 1<<i;
   for(char mask = 1<<i; arg & mask != 0; mask>>=1)
      result |= mask;
   for(char mask = 1<<i; arg & mask != 0; mask<<=1)
      result |= mask;
   return result;
}

但它似乎有点难看。我怎么能比这更好呢?

2 个答案:

答案 0 :(得分:3)

这是一个有趣的操作。你写的代码表达得相当好,所以你会想一想它是如何丑陋的吗?

我可以看到的细节:鉴于i表示arg中的位数,i中更宽泛的类型绝对没有意义。在一个条件下编写!= 0从来都没有意义。您可能不希望在使用它的任何地方重新声明掩码,也不想连续两次初始化它。

至于实际的扩散位掩码,我想不出现在更具表现力,更清晰或更有效的方式。

答案 1 :(得分:0)

警告:这些都没有经过测试甚至是相关的*,但它可能很有趣。

隔离最右边的1s非常简单,例如:x ^ (x & ((x|(x-1))+1))(下面的说明),让我们一起使用。

第一个x|(x-1)将最右边的1涂抹到右边,将所有这些位加1圈,包括最右边的1个,并且x删除最右边的1个,最后,xoring x只留下最右边的1s。

然后我们只需要确保我们正在寻找的范围是最右边的。这不太适合简单的bitmath,但如果有Count Leading Zeros(clz),那就不太难了:

int shift = 32 - clz(~x & ((1 << i) - 1));  //replace 32 with word size
x = (x >> shift) << shift;

((1 << i) - 1)制作了我们正在寻找的游戏右端所在的部分的掩码(它也可能只是错过了结尾,但没关系) ,然后clz查找ix右侧的第一个零,然后移位移除我们不想查看的位。

应用第一个公式,用于隔离最右边的1s运行结果,以获得i所在的运行结果。i最好在某些运行中运行,或者事情侧身(更准确地说,它将返回从索引高于i开始的第一轮1s)

*:对于这个问题,这一切都不重要。一个2KB的表并不是一个笨拙的解决方案,除非你只有很少的可用内存,即使是这样,输入也很短,所以循环并不是那么糟糕。