为什么以下C代码不能用于为负数返回-1,为0返回0,为正数返回1?
(((x >> 31) ^ -1) + 1) | (!x ^ 1);
具体来说,当我传入负数时,它返回1.虽然看起来我有一个负数(即31位移位后最低有效位为1),但是它与-1进行异或运算会给我-2(即所有1和最低有效位的0),加1会使它为-1。
答案 0 :(得分:1)
>>
通常会对签名数据进行算术移位,因此((-1) >> 31) == (-1)
与您的假设相反。正如其他人所指出的那样,标准并不能保证这一点,但在现代系统中很可能是这样。在任何情况下,要小心这种类型的钻头。如果可移植性是一个问题或速度不是,你应该以不同的方式。有关一些想法,请参阅Is there a standard sign function (signum, sgn) in C/C++?。
答案 1 :(得分:1)
根据C99标准,如果x为负,则x >> n
的结果是实现定义的。所以你遇到问题的原因取决于你的编译器和架构。
但是,当你移动它时,x很可能是符号扩展,即重复最高位以保持符号与操作数相同。这是我的编译器发生的事情。因此,对于任何负数,x >> 31
为-1
。此外,对于任何非零数字!x
都是0(即假)。这适用于假设x是32位整数。如果使x成为unsigned int,它应该可以工作,但请考虑以下替代方法:
(x < 0) ? -1 : ((x > 0) ? 1 : 0)
我认为这有点不那么神秘了。
这是一个程序,您可以使用它来查看您的表达式正在做什么
#include <stdio.h>
#define EVALUATE(x) printf("%s = %d\n", #x, x)
int main(int argc, char** argv)
{
unsigned int x = 51;
EVALUATE(x >> 31);
EVALUATE(((x >> 31) ^ -1));
EVALUATE(((x >> 31) ^ -1) + 1);
EVALUATE(!x);
EVALUATE(!x ^ 1);
EVALUATE((((x >> 31) ^ -1) + 1) | (!x ^ 1));
return 0;
}