需要一些帮助 - 左移按位运算符
Need some help - left shift bitwise operator
我试图清楚地理解移位运算符的行为(尤其是对于边界情况),所以我设计了一个用 C++ 编写的小测试。
int a = odd_value; //321 in my case but it should not matter (the last bit to be 1)
print(0, a);
a = a << 31; // (this gives a segmentation fault, as normal because it tries the sign bit becomes 1 but all the other bits are 0).
print(0, a); //the segmentation fault happens here - it prints the minimum integer value and then gives the fault
a = (a << 1) + 1; // but if then I do this, shouldn't it set a to -1 ??
print(a); //gives 0
void print(int stackCallIndex, int nb)
{
if(nb)
{
print(++stackCallIndex, nb >> 1);
if(nb & 1) printf("%d", 1);
else printf("%d", 0);
if(stackCallIndex % 8 == 0) printf(" ");
}
}
如果您想要标准行为,那么它是未定义的。根据 [expr.shift]:
The value of E1 << E2
is E1
left-shifted E2
bit positions; vacated bits are zero-filled. If E1
has an unsigned
type, [...]. Otherwise, if E1
has a signed type and non-negative value, and E1
×2
E2 is representable in the corresponding unsigned type of the result type, then that value, converted to the result type, is the
resulting value; otherwise, the behavior is undefined.
Any (odd number > 1) x 231 不能用 uint32_t
表示,因此行为未定义。您的编译器显然选择将此实现为分段错误,这是完全符合要求的行为(编辑:呃,至少它会是,如果那是它发生的地方)。
一种更典型的方法是让位 "fall off" 结束。也就是说,奇数的 a << 31
将变为 0x80000000
。但即使在那种情况下,1 的另一个左移将导致 0
,因此您必须减去 1 才能得到 -1
,而不是添加 1
。
当我尝试得到 -214783648 时,它是整数中已知的最小值...这意味着您生成的整数大于允许的范围,这就是为什么在您的情况下会出现分段错误...
根据您的代码,如果您尝试打印负值,我敢打赌由于无限递归会导致 Whosebug。
void print(int stackCallIndex, int nb)
{
if(number)
{
print(++stackCallIndex, nb >> 1); // likely an infinite recursion here.
if(nb & 1) printf("%d", 1);
else printf("%d", 0);
if(stackCallIndex % 8 == 0) printf(" ");
}
}
那为什么会是无限递归呢?严格按照标准右移负符号整数是实现定义的。
在大多数实现中,它会执行 arithmetic right shift,这意味着 1111 1100
(二进制补码 8 位中的 -4)向右移动 1 将导致 1111 1110
(-2在二进制补码 8 位中)正如您所看到的,您总是再次填充符号位,因此您的数字永远不会达到 0,并且 if 条件始终为真。
通常对有符号值进行位操作不是一个好主意,它们在少数情况下涉及 implementation/undefined 行为。在使用位操作之前最好将所有值都转换为无符号。
我试图清楚地理解移位运算符的行为(尤其是对于边界情况),所以我设计了一个用 C++ 编写的小测试。
int a = odd_value; //321 in my case but it should not matter (the last bit to be 1)
print(0, a);
a = a << 31; // (this gives a segmentation fault, as normal because it tries the sign bit becomes 1 but all the other bits are 0).
print(0, a); //the segmentation fault happens here - it prints the minimum integer value and then gives the fault
a = (a << 1) + 1; // but if then I do this, shouldn't it set a to -1 ??
print(a); //gives 0
void print(int stackCallIndex, int nb)
{
if(nb)
{
print(++stackCallIndex, nb >> 1);
if(nb & 1) printf("%d", 1);
else printf("%d", 0);
if(stackCallIndex % 8 == 0) printf(" ");
}
}
如果您想要标准行为,那么它是未定义的。根据 [expr.shift]:
The value of
E1 << E2
isE1
left-shiftedE2
bit positions; vacated bits are zero-filled. IfE1
has an unsigned type, [...]. Otherwise, ifE1
has a signed type and non-negative value, andE1
×2
E2 is representable in the corresponding unsigned type of the result type, then that value, converted to the result type, is the resulting value; otherwise, the behavior is undefined.
Any (odd number > 1) x 231 不能用 uint32_t
表示,因此行为未定义。您的编译器显然选择将此实现为分段错误,这是完全符合要求的行为(编辑:呃,至少它会是,如果那是它发生的地方)。
一种更典型的方法是让位 "fall off" 结束。也就是说,奇数的 a << 31
将变为 0x80000000
。但即使在那种情况下,1 的另一个左移将导致 0
,因此您必须减去 1 才能得到 -1
,而不是添加 1
。
当我尝试得到 -214783648 时,它是整数中已知的最小值...这意味着您生成的整数大于允许的范围,这就是为什么在您的情况下会出现分段错误...
根据您的代码,如果您尝试打印负值,我敢打赌由于无限递归会导致 Whosebug。
void print(int stackCallIndex, int nb)
{
if(number)
{
print(++stackCallIndex, nb >> 1); // likely an infinite recursion here.
if(nb & 1) printf("%d", 1);
else printf("%d", 0);
if(stackCallIndex % 8 == 0) printf(" ");
}
}
那为什么会是无限递归呢?严格按照标准右移负符号整数是实现定义的。
在大多数实现中,它会执行 arithmetic right shift,这意味着 1111 1100
(二进制补码 8 位中的 -4)向右移动 1 将导致 1111 1110
(-2在二进制补码 8 位中)正如您所看到的,您总是再次填充符号位,因此您的数字永远不会达到 0,并且 if 条件始终为真。
通常对有符号值进行位操作不是一个好主意,它们在少数情况下涉及 implementation/undefined 行为。在使用位操作之前最好将所有值都转换为无符号。