在被建议阅读“ Stanley B. Lipman编辑的C ++ Primer 5”之后,我不明白这一点:
第66页。“涉及无符号类型的表达式”
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
他说:
在第二个表达式中,在完成加法之前,将int值-42转换为unsigned。将负数转换为无符号的行为与我们试图将该负值分配给无符号对象的行为完全相同。如上所述,值“环绕”。
但是,如果我做这样的事情:
unsigned u = 42;
int i = -10;
std::cout << u + i << std::endl; // Why the result is 32?
如您所见,-10
未转换为unsigned int
。这是否意味着在将signed integer
提升为unsigned integer
之前进行了比较?
答案 0 :(得分:26)
-10
将被转换为具有非常大值的无符号整数,您得到一个小数字的原因是加法运算会回绕您。使用32位无符号整数-10
与4294967286
相同。当您加上42时,您得到4294967328
,但是最大值为4294967296
,因此我们必须以4294967328
为模数4294967296
来取32
。
答案 1 :(得分:17)
好吧,我想这是“两个错误不会造就一个错误”的例外:)
实际情况是,引擎盖下实际上有两个环绕(无符号溢出),最终结果在数学上是正确的。
首先,i
转换为无符号,并且根据环绕行为,其值为std::numeric_limits<unsigned>::max() - 9
。
当将此值与u
求和时,数学结果将为std::numeric_limits<unsigned>::max() - 9 + 42 == std::numeric_limits<unsigned>::max() + 33
,这是一个溢出,我们得到了另一个折回。所以最终结果是32
。
作为算术表达式的一般规则,如果您只有无符号的溢出(无论多少),并且如果最终的数学结果在表达式数据类型中是可表示的,则表达式的值将是数学上正确的值。这是由于以下事实导致的:C ++中的无符号整数遵守2 n 模的算术定律(请参见下面的内容)。
重要提示。根据C ++,无符号算术不会溢出:
第6.9.1节基本类型[basic.fundamental]
- 无符号整数应服从2 n 模算术定律 是该特定值的值表示形式中的位数 整数 49
的大小49)这意味着无符号算术不会溢出,因为 结果不能用无符号整数表示 类型以与最大数相比大一的形式取模 可以用结果无符号整数类型表示的值。
但是,我将在答案中保留“溢出”以表示常规算术无法表示的值。
我们通俗地称为“环绕”的实际上是无符号整数的算术模数性质。但是,我也会使用“环绕”,因为它更易于理解。
答案 2 :(得分:5)
i
实际上被提升为unsigned int
。
C和C ++中的
无符号整数在ℤ/ 2 n ℤ中实现算术,其中n是无符号整数类型的位数。这样我们得到
[42] + [-10]≡[42] + [2 n -10]≡[2 n + 32]≡[32],>
,其中[x]表示x / 2 n ℤ中x的等价类。
当然,中间选择每个等价类的非负代表的中间步骤虽然是正式发生的,但并不需要解释结果;立即
[42] + [-10]≡[32]
也是正确的。
答案 3 :(得分:3)
“在第二个表达式中,在完成加法之前,将int值-42转换为unsigned”
是的,这是真的
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。全部是在未签名的情况下完成的
答案 4 :(得分:2)
这是关于2的补码表示的奇妙部分。处理器不知道也不关心数字是带符号的还是无符号的,操作是相同的。在两种情况下,计算都是正确的。实际上,只有在打印后才对二进制数进行解释,这实际上很重要(可能还有其他情况,例如比较运算符)
-10 in 32BIT binary is FFFFFFF6
42 IN 32bit BINARY is 0000002A
将它们加在一起,对它们是有符号的还是无符号的,对处理器都没有关系,结果是:100000020。在32位中,开头的1将放置在溢出寄存器中,而在c ++中则消失。结果为0x20,即32。
在第一种情况下,基本上是相同的:
-42 in 32BIT binary is FFFFFFD6
10 IN 32bit binary is 0000000A
将它们加在一起并得到FFFFFFE0
FFFFFFE0作为带符号的int是-32(十进制)。计算正确!但是,由于它被打印为无符号,因此显示为4294967264。这是关于解释结果的原因。