我使用的语言是C. x和n的类型是int。
我有一行代码如下
printf("x=%x,n=%d,first=%x,second=%x\n",x,n,((~(x+0xffffffff))>>n),((~x+1)>>n));
它显示了x,n的值和两个移位x的补数的n位的方法。 当x = 0x80000000,〜(x + 0xffffffff)= 0x8000000,~x + 1 = 0x80000000时,当将这两个移位n位时,结果是不同的。
顺便说一下,如果我把0xffffffff改为~1 + 1(即〜(x +(~1 + 1)),结果与~x + 1相同
我想知道为什么会这样。感谢。
答案 0 :(得分:4)
Pavan Manjunath现在删除的答案对于一个案例有正确的答案,假设int
和32位类型一样。整数常量
0xffffffff
的值为2^32 - 1
且int
无法表示,但可以表示为unsigned int
。所以它的类型是unsigned int
(6.4.4.1)。因此x
转换为unsigned int
以进行添加,
((~(x+0xffffffff))>>n)
评估为
((~(0x80000000u + 0xffffffffu)) >> n)
((~0x7fffffffu) >> n)
(0x80000000u >> n)
如果2^(31-n)
,则值为0 <= n < 32
(如果n
超出该范围,则为未定义的行为。)
对于另一种情况,ouah的回答是正确的,当x = 0x80000000
是int
时,~0x8000000 = 0x7fffffff = INT_MAX
和INT_MAX + 1
是未定义的行为,因为有符号整数溢出。
然而,常见的行为是环绕,然后加法的结果是有符号整数0x80000000
,负整数的右移是实现定义的行为(6.5.7)。 Common正在使用符号扩展进行转换,这会生成结果-2^(31-n)
,然后unsigned int
转换说明符{{1}将其解释为2^32 - 2^(31-n)
,其值为printf
}}
答案 1 :(得分:1)
当x = 0x80000000时,〜(x + 0xffffffff)= 0x8000000,~x + 1 = 0x80000000,
在具有32位int
的系统上(假设x
的类型为int
)和二进制补码表示,此表达式为:
~x+1
是未定义的行为。 x = 0x80000000
表示~x == 0x7FFFFFFF == INT_MAX
,INT_MAX + 1
表示未定义的行为。因此~x + 1
可以是0x80000000
或其他任何内容。
这个表达式:
~(x+0xffffffff)
另一方面,被定义(C中的0xffffffff
是unsigned int
并且等于0x80000000
。它实际上是定义的,因为0xffffffff
是unsigned int
,无符号整数从不在C标准意义上溢出。
这意味着这句话:
printf("x=%x,n=%d,first=%x,second=%x\n",x,n,((~(x+0xffffffff))>>n),((~x+1)>>n));
调用未定义的行为,比较两个结果是没有意义的。
答案 2 :(得分:0)
(假设sizeof(int)是4;即32位有符号值)。 0x80000000的; // -2147483648这是最小可能的负int 0xFFFFFFFF //是-1
将两者加在一起会导致从负面到正面的“环绕” 0x7FFFFFFF是两者的总和(使用int算术),即2147483647
在0x7FFFFFFF上使用'〜'运算符会产生逐位完整,或0x80000000
如果你从任何int值开始并从中减去1(或者加1,它没关系)足够多次,你将使它翻转它的符号。这是使用固定精度的算术的基本问题。
在您的情况下,您不能期望混合已签名的算术运算符和按位运算符,而不必非常注意这种限制情况。
另请注意,使用2的补码算法时存在不对称性:还有一个负数比正数(因为您需要表示零,这会为其余值留下奇数个其他位表示。)< / p>