【问题标题】:C++ left shift overflow for negative numbers负数的 C++ 左移溢出
【发布时间】:2019-09-01 01:37:36
【问题描述】:

如下图,代码有bug。当输入 a = -1, b = 1 时,其中一行出现运行时错误。您能帮我理解 (a & b) 是如何变成 INT_MIN 的吗?

在我的理解中,-1 表示为 32 位二进制格式 0xFFFFFFFF,1 表示为 32 位格式 0x00000001,因此 (-1 & 1) 将变为 0x00000001。我的 python 命令也显示 '0b1' 作为结果。为什么会报INT_MIN的错误?

int getSum(int a, int b) {
    while (b != 0) {
        int carry = (a & b) << 1;//BUG! left shift of negative value -2147483648
        a = a ^ b;
        b = carry;
    }

    return a;
}

更新:负数的右移是否定义明确?只要满足下面引用的要求,右移负数似乎是可以的。

来自 cppreference.com,

对于无符号 a 和有符号 a 具有非负值,值 a >> b 是 a/2b 的整数部分。对于负 a,a 的值 >> b 是实现定义的(在大多数实现中,这执行 算术右移,因此结果仍然为负)。

在任何情况下,如果右操作数的值为负数或 大于或等于提升的左操作数中的位数, 行为未定义。

【问题讨论】:

  • 我们知道用二进制表示负数的方式不止一种,而不仅仅是 0xFFFFFFFF en.wikipedia.org/wiki/Signed_number_representations
  • 请注意,XOR 不会产生比操作数更多的位,因此它永远不会溢出(可能在具有陷阱表示的系统上除外)Can XOR of two integers go out of bounds?。 INT_MIT 在int 的范围内,因此它不是溢出
  • 第一个a &amp; b1 如您所说,但您处于更新ab 值的while 循环中,该错误出现在稍后的迭代中
  • 在 C 中使用 unsigned types 进行位旋转。

标签: c bit-manipulation


【解决方案1】:

假设这是 C 或 C++,您的错误是因为 Left Shifting a negative value is Undefined Behavior,并且左移有符号值使其大于 MAX_INT 也是未定义行为。

如果您在运行此序列时检查ab 的值,您将得到:

-1 1
-2 2
-4 4
...
-1073741824 1073741824

此时a&amp;b == 1073741824。但左移 1 与乘以 2 相同,得到2147483648,大于 INT_MAX。

这是未定义的行为。系统可以选择做任何事情。看来,在你的情况下,它做了位移给0x80000000。在签名的int 中,这表示 INT_MIN。所以下一次循环时,你试图左移一个负数,这又是未定义的行为。您的系统选择将此视为例外。

一般来说,如果你在做位操作,你最好使用无符号类型。

【讨论】:

  • 非常感谢!在这种情况下,当 a = -1 且 b = 1 时,我们应该如何处理左移?本质上,getSum() 函数是通过位操作计算 a + b。
  • 转换为无符号。
  • 谢谢,您能详细解释一下吗?
猜你喜欢
  • 1970-01-01
  • 2011-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多