一个负整数与一个更大的无符号整数相加是否被提升为无符号整数?

Is a negative integer summed with a greater unsigned integer promoted to unsigned int?

建议阅读后 "C++ Primer 5 ed by Stanley B. Lipman" 我不明白:

第 66 页。"Expressions Involving Unsigned Types"

unsigned u = 10;
int i = -42;
std::cout << i + i << std::endl; // prints -84
std::cout << u + i << std::endl; // if 32-bit ints, prints 4294967264

他说:

In the second expression, the int value -42 is converted to unsigned before the addition is done. Converting a negative number to unsigned behaves exactly as if we had attempted to assign that negative value to an unsigned object. The value “wraps around” as described above.

但是如果我这样做:

unsigned u = 42;
int i = -10;
std::cout << u + i << std::endl; // Why the result is 32?

如您所见,-10 未转换为 unsigned int。这是否意味着在将 signed integer 提升为 unsigned integer 之前进行比较?

嗯,我想这是 "two wrongs don't make a right" 的一个例外:)

发生的事情是实际上有两个环绕(无符号溢出),最终结果在数学上是正确的。

  • 首先,i 被转换为无符号,并且根据回绕行为,值为 std::numeric_limits<unsigned>::max() - 9.

  • 当这个值与 u 相加时,数学结果将是 std::numeric_limits<unsigned>::max() - 9 + 42 == std::numeric_limits<unsigned>::max() + 33,这是一个溢出,我们得到另一个环绕。所以最后的结果是32.


作为算术表达式中的一般规则,如果只有无符号溢出(无论有多少)并且如果最终的数学结果可以用表达式数据类型表示,那么表达式的值将是数学上正确的一。这是因为 C++ 中的无符号整数遵守算术模 2n 的法则(见下文)。


重要通知。根据 C++ 无符号算术不会溢出:

§6.9.1 Fundamental types [basic.fundamental]

  1. Unsigned integers shall obey the laws of arithmetic modulo 2n where n is the number of bits in the value representation of that particular size of integer 49

49) This implies that unsigned arithmetic does not overflow because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting unsigned integer type.

然而,我会在我的回答中留下 "overflow" 来表达无法用常规算术表示的值。

我们俗称的"wrap around"实际上只是无符号整数的算术模性质。然而,我会使用 "wrap around",因为它更容易理解。

-10 正在转换为一个具有非常大值的无符号整数,你得到一个小数字的原因是加法将你包裹起来。对于 32 位无符号整数,-104294967286 相同。当你将 42 添加到你得到 4294967328,但最大值是 4294967296,所以我们必须取 42949673284294967296,我们得到 32

i实际上提升为unsigned int.

C和C++中的无符号整数在ℤ/2nℤ中实现算术,其中n是无符号整数类型中的位数。因此我们得到

[42] + [-10] ≡ [42] + [2n - 10] ≡ [2n + 32 ] ≡ [32],

with [x]表示x在ℤ / 2nℤ中的等价class.

当然,中间步骤只选择每个等价的非负代表class,虽然它正式发生,但没有必要解释结果;立即

[42] + [-10] ≡ [32]

也是正确的。

"In the second expression, the int value -42 is converted to unsigned before the addition is done"

是的,这是真的

unsigned u = 42;
int i = -10;
std::cout << u + i << std::endl; // Why the result is 32?

假设我们是 32 位的(在 64b 中没有任何变化,这只是为了解释)这被计算为 42u + ((unsigned) -10) 所以 42u + 4294967286u 结果是 4294967328u 被截断为 32 位所以 32 . 全部在 unsigned

中完成

这是 2 的补码表示的精彩之处的一部分。处理器不知道或不关心数字是有符号的还是无符号的,操作是相同的。在这两种情况下,计算都是正确的。只有在打印时如何解释二进制数才是真正重要的(可能还有其他情况,如比较运算符)

-10 in 32BIT binary is FFFFFFF6
42 IN 32bit BINARY is  0000002A

相加,有符号无符号对处理器没有影响,结果是:100000020。在32bit中,开头的1会放在溢出寄存器中,而在c++中是只是消失了。结果为 0x20,即 32。

第一种情况,基本相同:

-42 in 32BIT binary is FFFFFFD6
10 IN 32bit binary is 0000000A

将它们相加得到 FFFFFFE0

FFFFFFFE0 作为有符号整数是 -32(十进制)。计算正确!但是,因为它被打印为无符号的,所以它显示为 4294967264。这是关于解释结果。