bit twiddling :检查非负整数作为 2 的幂的差

bit twiddling : checking non-negative integers as difference of powers of 2

问题:检查非负整数是否具有 2^j - 2^k where j>=k>=0 形式,即 2 的幂差。 我的解决方案:数字 n (say) 可以表示为连续的 1 序列 for eg. 00011110。我将关闭 1 的连续序列(最右边)并对 n 进行零检查。 我在这里做的是,steps for solution 00011110 00011111(turn on trailing 0's) 00000000(then turn off trailing 1's)。 使用这个公式 (x | (x - 1)) & ((x | (x - 1)) + 1)。 但是不使用文字的更有效的公式(可能是因为操作次数较少)是 ((x & -x) + x) & x 后跟零检查。我无法理解这一点,但它写的是做同样的事情,只是无法从我的结果中推导出公式。谁可以给我解释一下这个?

编辑:32 位字,2 的补码

鉴于 -x~x + 1,如果数字的形式为 2^j - 2^k 那么:

  • -x = 2^k 加上所有 1 >= 2^j,因为进位会波动直到达到 2^k,然后停止;
  • 因此 x & -x= 2^k;
  • 因此(x & -x) + x = 2^k;和
  • 因此 ((x & -x) + x) & x = 0.

并且您可以沿着该逻辑向后工作:

  • ((x & -x) + x) & x = 0 => ((x & -x) + x) 和 x 之间没有公共位;
  • x 和 ((x & -x) + x) 之间没有公共位意味着对于 x 中连续的 1 组,(x & -x) 必须设置这些位中的最低位并且 none 其他的;
  • ...考虑到携带涟漪的方式,实现这一目标的唯一方法是如果只有一组连续的 1。

您要求提供连接两个表达式的代数证明,所以这里有一个,但有一些不简单的步骤

((x | (x - 1)) + 1) & (x | (x - 1))
// rename x | (x - 1) into blsfill(x)
(blsfill(x) + 1) & blsfill(x)
// the trailing zeroes that get filled on the right side of the & don't matter,
// they end up being reset by the & anyway
(blsfill(x) + 1) & x
// filling the trailing zeroes and adding 1,
// is the same thing as skipping the trailing zeroes and adding the least-set-bit
(x + blsi(x)) & x
// rewrite blsi into elementary operations
(x + (x & -x)) & x