我正在使用Coursera上的硬件/软件界面课程,从那里我确定你已经看到了其中一些问题..
其中一个任务是确定2的补码整数x是否可以适合n位,仅使用运算符的子集等。我做得很好,但遇到了两种不同的方法,并用它们点击白板两者都要完全理解它们。然后是混乱。
如果可能,函数返回1,否则返回0.
int fooBar(int x, int n) {
return !(((x << (32-n)) >> (32-n)) ^x);
}
以正整数执行
fooBar(5,3) == 0
正确计算5不能表示为2的补码3位整数。
以负整数执行
fooBar(-4,3) == 1
正确计算-4可以表示为2的补码3位整数。
方法1分析
x=5, n=3
00000000000000000000000000011101 32 - n == 29
10100000000000000000000000000000 x<<29
00000000000000000000000000000101 >> 29
00000000000000000000000000000101 5
XOR
00000000000000000000000000000101 5
--------------------------------
00000000000000000000000000000000 0
00000000000000000000000000000001 !0 == answer, 1.
如您所见,这将返回0,因为没有5可能不会表示为3位内的2的补码整数。
int fooBar(int x, int n) {
return !((x & ~(1 << (32-n))) ^x);
}
以正整数执行
fooBar(5,3) == 1
误报。
以负整数执行
fooBar(-4,3) == 0
假阴性。
方法2分析
x=5, n=3
00000000000000000000000000011101 32 - n == 29
11011111111111111111111111111111 ~(1<<29)
AND
00000000000000000000000000000101 5
--------------------------------
00000000000000000000000000000101 5
00000000000000000000000000000101 5
XOR
00000000000000000000000000000101 5
--------------------------------
00000000000000000000000000000000 0
00000000000000000000000000000001 !0 == answer, 1.
我正在编译:
gcc version 4.7.2 (Debian 4.7.2-5)
问题
我无法解释输出的差异,当分析表明在位级别的所有内容都是相同的时候,所以关于我可以在哪里为自己揭示这一点的任何提示/提示都是高度的赞赏。
感谢您的时间!
SC。
答案 0 :(得分:3)
在方法1中,你写了
10100000000000000000000000000000 x<<29
00000000000000000000000000000101 >> 29
但这不正确(请注意,您的分析意味着fooBar(5,3) == 1
)。
首先,5 << 29
的结果会导致签名的32位(或更小)int
溢出,这是未定义的行为。
接下来,如果移位创建指示的位模式(通常如此),结果将是负数。
右移负整数是实现定义的,常见的是算术右移,它会签名扩展,这里导致
11111111111111111111111111111101 >> 29
当xor-ed为5时给出非零结果(然后应用!
产生0)。
方法2根本不起作用,因为除了某些输入的未定义行为外,它所做的只是检查(32-n)
位是否已设置。