是无符号整数减法定义的行为?

我遇到过一些人的代码,他们似乎认为在结果为负数时,从另一个相同types的整数中减去一个无符号整数是有问题的。 所以这样的代码即使发生在大多数架构上也不正确。

unsigned int To, Tf; To = getcounter(); while (1) { Tf = getcounter(); if ((Tf-To) >= TIME_LIMIT) { break; } } 

这是我能find的C标准中唯一相关的引用。

涉及无符号操作数的计算永远不会溢出,因为无法用结果无符号整数types表示的结果被减less的模数大于可由结果types表示的最大值的数。

我想可以把这个引号的意思是,当右操作数较大时,操作被调整为在模截断数的上下文中是有意义的。

0x0000 – 0x0001 == 0x 1 0000 – 0x0001 == 0xFFFF

而不是使用实现相关的签名语义:

0x0000 – 0x0001 ==(无符号)(0 + -1)==(0xFFFF也是0xFFFE或0x8001)

哪个或哪个解释是正确的? 它是否定义?

生成一个无符号types的负数的减法结果是明确的:

  1. […]涉及无符号操作数的计算永远不会溢出,因为无法用结果无符号整数types表示的结果被减less的模数大于可由结果types表示的最大值的数。 (ISO / IEC 9899:1999(E)§6.2.5/ 9)

正如你所看到的, (unsigned)0 - (unsigned)1等于-1模UINT_MAX + 1,换句话说,UINT_MAX。

请注意,尽pipe它确实会说“涉及无符号操作数的计算永远不会溢出”,这可能会导致您认为它只适用于超出上限,这是作为实际绑定部分句子的动机 :“a无法用结果的无符号整数types表示的结果被减less的模数大于可以由结果types表示的最大值的数字“。 这个短语并不局限于types上限的溢出,同样适用于太低而不能表示的值。

使用无符号types时, 模块化算术 (也称为“环绕”行为)正在发生。 要理解这个模块化算术 ,只需看看这些时钟:

在这里输入图像描述

9 + 4 = 113模12 ),所以到另一个方向是: 1 – 4 = 9-3模12 )。 处理无符号types时应用相同的原理。 如果结果typesunsigned ,则进行模运算。


现在看下面的操作,将结果存储为一个unsigned int

 unsigned int five = 5, seven = 7; unsigned int a = five - seven; // a = (-2 % 2^32) = 4294967294 int one = 1, six = 6; unsigned int b = one - six; // b = (-5 % 2^32) = 4294967291 

当你想确保结果是有signed ,然后把它存储到有signedvariables中或者把它转换为有signed 。 当你想得到数字之间的差异,并确保模块化算术不会被应用,那么你应该考虑使用stdlib.h定义的abs()函数:

 int c = five - seven; // c = -2 int d = abs(five - seven); // d = 2 

要特别小心,特别是在写作条件的时候,因为:

 if (abs(five - seven) < seven) // = if (2 < 7) // ... if (five - seven < -1) // = if (-2 < -1) // ... if (one - six < 1) // = if (-5 < 1) // ... if ((int)(five - seven) < 1) // = if (-2 < 1) // ... 

 if (five - seven < 1) // = if ((unsigned int)-2 < 1) = if (4294967294 < 1) // ... if (one - six < five) // = if ((unsigned int)-5 < 5) = if (4294967291 < 5) // ... 

那么,第一个解释是正确的。 然而,在这种情况下,你对“签名语义”的推理是错误的。

再一次,你的第一个解释是正确的。 无符号math运算遵循模算术的规则,即对于32位无符号types, 0x0000 - 0x0001计算结果为0xFFFF

然而,第二种解释(基于“签名语义”的解释)也需要产生相同的结果。 也就是说,即使您在有符号types的域中计算0 - 1 ,并获得-1作为中间结果,当它被转换为无符号types时,仍然需要产生0xFFFF 。 即使某些平台对有符号整数使用了一个奇异的表示(1的补码,符号幅度),该平台在将有符号整数值转换为无符号整数值时仍然需要应用模algorithm的规则。

例如,这个评估

 signed int a = 0, b = 1; unsigned int c = a - b; 

仍然保证在c产生UINT_MAX ,即使平台使用有符号整数的奇异表示。

使用unsigned unsigned inttypes或更大的无符号数字,在没有types转换的情况下, ab被定义为产生无符号数字,当将其添加到b ,将产生a 。 将负数转换为无符号被定义为产生一个数字,当加到符号反转的原始数字上时,该数字将产生零(所以将-5转换为无符号将产生一个当加到5时将产生零的值) 。

请注意,小于unsigned int无符号数可能会在减法之前被提升为inttypes, ab的行为将取决于int的大小。