【问题标题】:Using 1's complement to generate a mask that shows the first non-zero bit使用 1 的补码生成显示第一个非零位的掩码
【发布时间】:2016-11-07 02:07:47
【问题描述】:

我在阅读面试准备书时发现了一个关于 1 的补码的有趣属性。

该属性表示给定一个数字 X,我们可以使用 1 的补码生成一个显示第一个设置位(从右到左)的掩码,如下所示: X & ~(X - 1) 其中~ 代表 1 的补码。

例如,如果X = 0b0011 那么 0b0011 & 0b1101 = 0b0001

我了解到作者正在使用X-1 来翻转右侧的第一个非零位。但我很好奇他是如何想出这样的想法的十。

这是我第一次在 StackOverflow 上发帖,如果这个问题不属于这里,我深表歉意。

【问题讨论】:

标签: bit-manipulation bitwise-operators


【解决方案1】:

首先,请注意对于任何XX & (~X) = 0X & X = X

Let X = b_n b_(n-1) ... b_k ... b_1, where b_k is the first set bit.
Thus, X is essentially this:
    b_n   b_(n-1) ...  b_(k+1) 1 0 0 ... 0
                               ---- k ----
X-1 is:
    b_n   b_(n-1) ...  b_(k+1) 0 1 1 ... 1
                               ---- k ----
~(X-1) is:
    ~b_n ~b_(n-1) ... ~b_(k+1) 1 0 0 ... 0
                               ---- k ----

X & ~(X-1) is:
    0 0 .................... 0 1 0 0 ... 0
                               ---- k ----

【讨论】:

    【解决方案2】:

    这实际上可以使用一些数学来证明。让x 是一个正整数。对于所有x,存在x 的二进制表示。此外,对于所有x,存在一个数字x - 1,它也具有二进制表示。对于所有x1s 位置的位将与x - 1 的位不同。让我们将~ 定义为个数的补码运算符。对于任何二进制数b~bb 中的所有0s 转换为1s,并将b 中的所有1s 转换为0s。然后我们可以说~(x - 1) 必须在1s 位置与x 具有相同的位。现在,这对于奇数来说很简单,因为所有奇数o1s 位中都有一个1~(x - 1) 也必须如此,我们可以停在那里。对于偶数,这有点棘手。对于所有偶数e1 位必须为空。正如我们所说x(以及e)必须大于0,我们也可以说对于所有偶数e,存在一些位使得该位的值是@987654351 @。我们也可以说对于e - 11s 位必须是1,因为e - 1 必须是奇数。此外,我们可以说e 中值为1 的第一位将是e - 1 中的0。因此,使用e - 1 的反码,e 中必须是0 的位将根据反码规则变为1。使用& 运算符,这将是e~(e - 1) 之间常见的1 位。

    【讨论】:

      【解决方案3】:

      这个技巧可能更广为人知的写成

      X & -X
      

      根据定义(-)等价,使用-的以下解释变得非常容易理解:

      在非零数字的字符串表示法中,-(a10k) = (~a)10k

      如果您不熟悉该表示法,a10k 仅表示“一些位字符串 'a' 后跟 1 后跟 k 个零”。

      这种解释只是说否定保留所有尾随零和最低 1,但反转所有高位。您可以从否定的定义中看到它也是这样做的,例如,如果您查看~X + 1,您会看到 +1 取消了尾随零的反转(这成为 +1 进行的)和最低设置位(变为 0,然后通过尾随零的进位被它捕获)。

      无论如何,使用否定的解释,显然顶部被移除,最低设置位被保留,尾随零将保留。

      一般来说,字符串表示法在想出这些技巧时非常有用。例如,如果您知道否定在字符串表示法中看起来像这样,那么这个技巧真的很明显,您还可以找到一些相关的技巧:

      • x & x - 1 重置最低设置位
      • x | -x 保留尾随零,但设置所有高位
      • x ^ -x 保留尾随零,重置最低设置位,但设置所有高位

      .. 以及更多变体。

      【讨论】:

      • 不幸的是,这个版本在 C 中是未定义的。
      • @apt1002 如果您使用无符号数字,则不会,无论如何我在这里看不到 C 标记
      • 即便如此:stackoverflow.com/questions/3952123/… 。没错,没有 C 标签;我在推断为什么 OP 选择了他所做的形式,也许是错误的。
      • 啊,抱歉,我误读了您的评论。您似乎对无符号否定是正确的。
      • @apt1002 既然我们正在谈论 C - 如果这是推理,它有帮助吗?我对 C 的 UB 废话不是很熟悉,但是 -1 已经不安全,IIRC 允许补码创建无效值
      猜你喜欢
      • 2015-01-20
      • 2020-01-28
      • 1970-01-01
      • 2010-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-31
      • 2019-07-15
      相关资源
      最近更新 更多