c理解的逻辑转变
logical shift in c understanding
我目前正在尝试理解这种逻辑转变是如何运作的。
给定公式:
int logicalShift(int x, int n){
int mask = x >> 31 << 31 >> n << 1;
return mask^ (x>>n);
}
x = 0x87654321 and n = 4.
的测试 运行 给出了 logicalShift(x,n) = 0x08765432
在这里,我知道 x 在二进制中是 1000 0111 0110 0101 0100 0011 0010 0001
并且在掩码过程之后,
x >> 31 是 0000 0000 0000 0000 0000 0000 0000 0001
<< 31 就是 1000 0000 0000 0000 0000 0000 0000 0000
and >> n 就是 0000 1000 0000 0000 0000 0000 0000 0000
并且 << 1 是 0001 0000 0000 0000 0000 0000 0000 0000
。
做 mask^ (x>>n)
意味着用 0000 1000 0111 0110 0101 0100 0011 0010
对 0001 0000 0000 0000 0000 0000 0000 0000
进行异或运算,结果是 0001 1000 0111 0110 0101 0100 0011 0010
,即 0x18765432.
我换档错了吗?
x >> 31 is 0000 0000 0000 0000 0000 0000 0000 0001
不对,错了。你的 x
是 signed
整数而 num
是 0x87654321
这意味着最后一个 31st bit
是 1 or set
,所以当你右移 sign
位被复制,结果 x
为 1111 1111 1111 1111 1111 1111 1111 1111
要获得预期的输出,请将 num
作为 unsigned
类型。
int logicalShift(unsigned int x, int n){
/* some code */
}
请注意,它的实现依赖于如果你的处理器支持算术右移然后last bit(sign bit)
被复制到剩余的字节,如果它是逻辑右移那么它不会'吨。
是的,你做错了。当 >>
的左操作数是有符号整数时,编译器通常会执行 算术 右移,而不是逻辑右移。这在函数名称中有所暗示:如果 >>
执行逻辑右移,它可以直接 return x >> n
。该函数的要点是通过算术右移来实现逻辑右移。
算术右移的意思是右移时,左边的位不补零,而是补最左边的位(符号位)。因此:
x >> 31 is 0000 0000 0000 0000 0000 0000 0000 0001
x >> 31 实际上是 1111 1111 1111 1111 1111 1111 1111 1111
.
这段摘录是错误的:
int logicalShift(int x, int n){
int mask = x >> 31 << 31 >> n << 1;
return mask^ (x>>n);
}
当 x
为负时 x >> 31
的行为是实现定义的。 << 31
具有负值或如果它更改符号位具有未定义的行为。它会像在非常严格的假设下进行逻辑移位一样工作:
int
的宽度是32位
- 有符号整数以 2 的补码作为其内部表示
- 负数右移的行为算术右移,
- 负数左移的行为定义为 2 的补码表示逻辑左移。
这不符合 C 标准。但是,它在 int
为 32 位宽的任何 GCC 目标平台上的行为都符合预期,因为 GCC 将最后 3 个指定为真。
这段代码是错误的,因为要进行正确的逻辑移位,应该使用 unsigned
值。但是,要将这些 返回 为有符号值是很棘手的 - 通常不需要它。
鉴于 n
的值在限制范围内(非负且小于 x
的位宽度),此函数具有完全定义的逻辑右移行为:
unsigned int logicalShift(unsigned int x, int n) {
return x >> n;
}
因为它只做一个表达式,我们注意到不需要为这样的操作使用函数。
我目前正在尝试理解这种逻辑转变是如何运作的。 给定公式:
int logicalShift(int x, int n){
int mask = x >> 31 << 31 >> n << 1;
return mask^ (x>>n);
}
x = 0x87654321 and n = 4.
的测试 运行 给出了 logicalShift(x,n) = 0x08765432
在这里,我知道 x 在二进制中是 1000 0111 0110 0101 0100 0011 0010 0001
并且在掩码过程之后,
x >> 31 是 0000 0000 0000 0000 0000 0000 0000 0001
<< 31 就是 1000 0000 0000 0000 0000 0000 0000 0000
and >> n 就是 0000 1000 0000 0000 0000 0000 0000 0000
并且 << 1 是 0001 0000 0000 0000 0000 0000 0000 0000
。
做 mask^ (x>>n)
意味着用 0000 1000 0111 0110 0101 0100 0011 0010
对 0001 0000 0000 0000 0000 0000 0000 0000
进行异或运算,结果是 0001 1000 0111 0110 0101 0100 0011 0010
,即 0x18765432.
我换档错了吗?
x >> 31 is 0000 0000 0000 0000 0000 0000 0000 0001
不对,错了。你的 x
是 signed
整数而 num
是 0x87654321
这意味着最后一个 31st bit
是 1 or set
,所以当你右移 sign
位被复制,结果 x
为 1111 1111 1111 1111 1111 1111 1111 1111
要获得预期的输出,请将 num
作为 unsigned
类型。
int logicalShift(unsigned int x, int n){
/* some code */
}
请注意,它的实现依赖于如果你的处理器支持算术右移然后last bit(sign bit)
被复制到剩余的字节,如果它是逻辑右移那么它不会'吨。
是的,你做错了。当 >>
的左操作数是有符号整数时,编译器通常会执行 算术 右移,而不是逻辑右移。这在函数名称中有所暗示:如果 >>
执行逻辑右移,它可以直接 return x >> n
。该函数的要点是通过算术右移来实现逻辑右移。
算术右移的意思是右移时,左边的位不补零,而是补最左边的位(符号位)。因此:
x >> 31 is
0000 0000 0000 0000 0000 0000 0000 0001
x >> 31 实际上是 1111 1111 1111 1111 1111 1111 1111 1111
.
这段摘录是错误的:
int logicalShift(int x, int n){
int mask = x >> 31 << 31 >> n << 1;
return mask^ (x>>n);
}
当 x
为负时 x >> 31
的行为是实现定义的。 << 31
具有负值或如果它更改符号位具有未定义的行为。它会像在非常严格的假设下进行逻辑移位一样工作:
int
的宽度是32位- 有符号整数以 2 的补码作为其内部表示
- 负数右移的行为算术右移,
- 负数左移的行为定义为 2 的补码表示逻辑左移。
这不符合 C 标准。但是,它在 int
为 32 位宽的任何 GCC 目标平台上的行为都符合预期,因为 GCC 将最后 3 个指定为真。
这段代码是错误的,因为要进行正确的逻辑移位,应该使用 unsigned
值。但是,要将这些 返回 为有符号值是很棘手的 - 通常不需要它。
鉴于 n
的值在限制范围内(非负且小于 x
的位宽度),此函数具有完全定义的逻辑右移行为:
unsigned int logicalShift(unsigned int x, int n) {
return x >> n;
}
因为它只做一个表达式,我们注意到不需要为这样的操作使用函数。