按位运算和掩码

时间:2014-07-10 02:15:33

标签: c bit

我在理解这段代码的工作原理时遇到了问题。我理解当x是正数时,实际上只有(x&〜mark)有一个值;但是当x是负数时,无法确定这段代码正在做什么。

e.g。如果x是1100(-4),并且掩模是0001,则〜掩模是1110。 ((~x& mask)+(x&〜mask))的结果是0001 + 1100 = 1011(-3),我努力但是无法弄清楚这段代码在做什么,任何建议都有帮助

/* 
 * fitsBits - return 1 if x can be represented as an 
 *  n-bit, two's complement integer.
 *   1 <= n <= 32
 *   Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 15
 *   Rating: 2
 */
int fitsBits(int x, int n) {
  /* mask the sign bit against ~x and vice versa to get highest bit in x. Shift by n-1, and not. */
  int mask = x >> 31;

  return !(((~x & mask) + (x & ~mask)) >> (n + ~0));
}

3 个答案:

答案 0 :(得分:3)

注意:这是毫无意义的,只值得做学术练习。

代码做出以下假设(C标准无法保证):

  • int是32位(1个符号位后跟31个值位)
  • int使用2的补码
  • 表示
  • 右移一个负数会做算术移位,即用1填充符号位

有了这些假设,x >> 31将为正数或零数生成全位 - 0,为负数生成全位 - 1

因此(~x & mask) + (x & ~mask)的效果与(x < 0) ? ~x : x相同。

由于我们假设为2的补码,因此负数的~x-(x+1)

这样做的结果是,如果x为正,则保持不变。如果x为负数,则将其映射到范围[0, INT_MAX]。在2的补码中,负数与非负数完全相同,因此可行。

最后,我们右移n + ~0。在2的补码中,~0-1,因此这是n - 1。例如,如果我们向右移4位,我们将所有位移到最后;这意味着该数字可用1个符号位和4个值位表示。所以这个转变告诉我们数字是否适合。

把所有这些放在一起,这是一种神秘的写作方式:

int x;

if ( x < 0 )
    x = -(x+1);

// now x is non-negative
x >>= n - 1;    // aka. x /= pow(2, n-1) 

if ( x == 0 )
    return it_fits;
else
    return it_doesnt_fit;

答案 1 :(得分:1)

这是对它的抨击,遗憾的是很难轻易地总结出逐位逻辑。一般的想法是尝试右移x并查看它是否变为0,因为!0返回1.如果右移n-1次正数导致0,那么这意味着n位足以表示它。

我在下面调用ab的原因是由于负数允许按惯例增加一个额外的值。整数可以表示一定数量的值,该值的数量是偶数,表示所需的数字之一是0,因此剩下的是要在负数和正数之间分配的奇数个值。负数会得到一个额外的值(按惯例),这是abs(x)-1发挥作用的地方。

如果您有任何疑问,请与我们联系:

int fitsBits(int x, int n) {
    int mask = x >> 31;

    /* -------------------------------------------------
    // A: Bitwise operator logic to get 0 or abs(x)-1
    ------------------------------------------------- */
    // mask == 0x0 when x is positive, therefore a == 0
    // mask == 0xffffffff when x is negative, therefore a == ~x
    int a = (~x & mask);
    printf("a = 0x%x\n", a);

    /* -----------------------------------------------
    // B: Bitwise operator logic to get abs(x) or 0
    ----------------------------------------------- */
    // ~mask == 0xffffffff when x is positive, therefore b == x
    // ~mask == 0x0 when x is negative, therefore b == 0
    int b = (x & ~mask);
    printf("b = 0x%x\n", b);

    /* ----------------------------------------
    // C: A + B is either abs(x) or abs(x)-1
    ---------------------------------------- */
    // c is either:
    // x if x is a positive number
    // ~x if x is a negative number, which is the same as abs(x)-1
    int c = (a + b);
    printf("c = %d\n", c);

    /* -------------------------------------------
    // D: A ridiculous way to subtract 1 from n
    ------------------------------------------- */
    // ~0 == 0xffffffff == -1
    // n + (-1) == n-1
    int d = (n + ~0);
    printf("d = %d\n", d);

    /* ----------------------------------------------------
    // E: Either abs(x) or abs(x)-1 is shifted n-1 times
    ---------------------------------------------------- */
    int e = (c >> d);
    printf("e = %d\n", e);

    // If e was right shifted into 0 then you know the number would have fit within n bits
    return !e;
}

答案 2 :(得分:0)

您应该使用unsigned int代替int执行这些操作。

某些操作(如>>)在处理已签名的数字时会执行arithmetic shift而不是逻辑转换,您将会遇到这种意外结果。

enter image description here

  
    

二进制数的正确算术移位1.最高位的空位置用原始MSB 的副本填充而不是零。 - from Wikipedia

  

unsigned int尽管如此:

enter image description here

  
    

在逻辑移位中,移入零以替换丢弃的位。因此,逻辑和算术左移完全相同。

         

然而,由于逻辑右移将0位值插入最高有效位,而不是复制符号位,因此对于无符号二进制数是理想的,而算术右移对于有符号的二进制数是理想的。补码二进制数。 - from Wikipedia