我在理解这段代码的工作原理时遇到了问题。我理解当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));
}
答案 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位足以表示它。
我在下面调用a
和b
的原因是由于负数允许按惯例增加一个额外的值。整数可以表示一定数量的值,该值的数量是偶数,表示所需的数字之一是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而不是逻辑转换,您将会遇到这种意外结果。
二进制数的正确算术移位1.最高位的空位置用原始MSB 的副本填充而不是零。 - from Wikipedia
unsigned int
尽管如此:
在逻辑移位中,移入零以替换丢弃的位。因此,逻辑和算术左移完全相同。
然而,由于逻辑右移将0位值插入最高有效位,而不是复制符号位,因此对于无符号二进制数是理想的,而算术右移对于有符号的二进制数是理想的。补码二进制数。 - from Wikipedia