C语言 无符号整数减法是定义行为吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7221409/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 09:30:48  来源:igfitidea点击:

Is unsigned integer subtraction defined behavior?

cstandardsunsignedinteger-arithmetic

提问by bdonlan

I have come across code from someone who appears to believe there is a problem subtracting an unsigned integer from another integer of the same type when the result would be negative. So that code like this would be incorrect even if it happens to work on most architectures.

我遇到过某人的代码,他似乎认为当结果为负数时,从另一个相同类型的整数中减去一个无符号整数会出现问题。因此,即使它碰巧适用于大多数架构,这样的代码也是不正确的。

unsigned int To, Tf;

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

This is the only vaguely relevant quote from the C standard I could find.

这是我能找到的 C 标准中唯一模糊相关的引用。

A computation involving unsigned operands can never over?ow, 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 type.

涉及无符号操作数的计算永远不会溢出,因为无法由结果无符号整数类型表示的结果会以比结果类型可以表示的最大值大 1 的数为模减少。

I suppose one could take that quote to mean that when the right operand is larger the operation is adjusted to be meaningful in the context of modulo truncated numbers.

我想人们可以把这句话理解为当正确的操作数更大时,操作被调整为在模截断数字的上下文中有意义。

i.e.

IE

0x0000 - 0x0001 == 0x 10000 - 0x0001 == 0xFFFF

0x0000 - 0x0001 == 0x 10000 - 0x0001 == 0xFFFF

as opposed to using the implementation dependent signed semantics:

与使用依赖于实现的签名语义相反:

0x0000 - 0x0001 == (unsigned)(0 + -1) == (0xFFFF but also 0xFFFE or 0x8001)

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

Which or what interpretation is right? Is it defined at all?

哪个或什么解释是正确的?它是完全定义的吗?

回答by LihO

When you work with unsignedtypes, modular arithmetic(also known as "wrap around"behavior) is taking place. To understand this modular arithmetic, just have a look at these clocks:

当您使用无符号类型时,会发生模块化算术(也称为“环绕”行为)。要理解这种模块化算法,只需看看这些时钟:

enter image description here

在此处输入图片说明

9 + 4 = 1(13 mod 12), so to the other direction it is: 1 - 4 = 9(-3 mod 12). The same principle is applied while working with unsigned types. If the result typeis unsigned, then modular arithmetic takes place.

9 + 4 = 1( 13 mod 12),所以到另一个方向是:1 - 4 = 9( -3 mod 12)。处理无符号类型时应用相同的原则。如果结果类型unsigned,则进行模算术。



Now look at the following operations storing the result as an unsigned int:

现在看看以下将结果存储为 的操作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

When you want to make sure that the result is signed, then stored it into signedvariable or cast it to signed. When you want to get the difference between numbers and make sure that the modular arithmetic will not be applied, then you should consider using abs()function defined in stdlib.h:

当您想确保结果为 时signed,请将其存储到signed变量中或将其强制转换为signed. 当您想获得数字之间的差异并确保不会应用模算术时,您应该考虑使用中abs()定义的函数stdlib.h

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

Be very careful, especially while writing conditions, because:

要非常小心,尤其是在编写条件时,因为:

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)
    // ...

but

if (five - seven < 1)   // = if ((unsigned int)-2 < 1) = if (4294967294 < 1)
    // ...

if (one - six < five)   // = if ((unsigned int)-5 < 5) = if (4294967291 < 5)
    // ...

回答by bdonlan

The result of a subtraction generating a negative number in an unsigned type is well-defined:

在无符号类型中生成负数的减法结果是明确定义的:

  1. [...] A computation involving unsigned operands can never 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 type. (ISO/IEC 9899:1999 (E) §6.2.5/9)
  1. [...] 涉及无符号操作数的计算永远不会溢出,因为无法由结果无符号整数类型表示的结果会以比结果类型可以表示的最大值大 1 的数为模减少。(ISO/IEC 9899:1999 (E) §6.2.5/9)

As you can see, (unsigned)0 - (unsigned)1equals -1 modulo UINT_MAX+1, or in other words, UINT_MAX.

如您所见,(unsigned)0 - (unsigned)1等于 -1 模 UINT_MAX+1,即 UINT_MAX。

Note that although it does say "A computation involving unsigned operands can never overflow", which might lead you to believe that it applies only for exceeding the upper limit, this is presented as a motivationfor the actual binding part of the sentence: "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 type." This phrase is not restricted to overflow of the upper bound of the type, and applies equally to values too low to be represented.

请注意,虽然它确实说“涉及无符号操作数的计算永远不会溢出”,这可能会让您相信它仅适用于超出上限的情况,但这是作为句子实际绑定部分的动机提出的:“a无法由结果无符号整数类型表示的结果以比结果类型可以表示的最大值大 1 的数字为模减少。” 这句话不限于类型上限的溢出,同样适用于太低而无法表示的值。

回答by AnT

Well, the first interpretation is correct. However, your reasoning about the "signed semantics" in this context is wrong.

嗯,第一个解释是正确的。但是,您在此上下文中对“签名语义”的推理是错误的。

Again, your first interpretation is correct. Unsigned arithmetic follow the rules of modulo arithmetic, meaning that 0x0000 - 0x0001evaluates to 0xFFFFfor 32-bit unsigned types.

同样,您的第一个解释是正确的。无符号算术遵循模算术规则,这意味着32 位无符号类型的0x0000 - 0x0001计算结果0xFFFF为 。

However, the second interpretation (the one based on "signed semantics") is also required to produce the same result. I.e. even if you evaluate 0 - 1in the domain of signed type and obtain -1as the intermediate result, this -1is still required to produce 0xFFFFwhen later it gets converted to unsigned type. Even if some platform uses an exotic representation for signed integers (1's complement, signed magnitude), this platform is still required to apply rules of modulo arithmetic when converting signed integer values to unsigned ones.

但是,也需要第二种解释(基于“签名语义”的解释)才能产生相同的结果。即,即使您0 - 1在有符号类型的域中进行评估并获得-1作为中间结果,这-1仍然需要0xFFFF在稍后转换为无符号类型时产生。即使某些平台对有符号整数(1 的补码、有符号幅度)使用了一种奇异的表示形式,但在将有符号整数值转换为无符号整数值时,该平台仍然需要应用模运算规则。

For example, this evaluation

比如这个评价

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

is still guaranteed to produce UINT_MAXin c, even if the platform is using an exotic representation for signed integers.

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

回答by supercat

With unsigned numbers of type unsigned intor larger, in the absence of type conversions, a-bis defined as yielding the unsigned number which, when added to b, will yield a. Conversion of a negative number to unsigned is defined as yielding the number which, when added to the sign-reversed original number, will yield zero (so converting -5 to unsigned will yield a value which, when added to 5, will yield zero).

对于类型unsigned int或更大的无符号数,在没有类型转换的情况下,a-b定义为产生无符号数,当添加到 时b,将产生a。将负数转换为无符号数被定义为产生的数字,当添加到符号反转的原始数时,将产生零(因此将 -5 转换为无符号将产生一个值,当添加到 5 时,将产生零) .

Note that unsigned numbers smaller than unsigned intmay get promoted to type intbefore the subtraction, the behavior of a-bwill depend upon the size of int.

请注意,小于 的无符号数unsigned int可能会int在减法之前被提升为类型, 的行为a-b将取决于 的大小int