在 C++ 未定义行为中,是否从较小的无符号值中减去较大的无符号值?

Is subtracting larger unsigned value from smaller in C++ undefined behaviour?

在下面的程序中,在 "integral promotions" 和 "usual arithmetic conversions" 之后,运算符 - 的两个操作数仍然是 unsigned long long。之后,C++11 标准说:

5.7.3 The result of the binary - operator is the difference resulting from the subtraction of the second operand from the first.

该标准是否在任何地方更详细地定义了减法的精确执行方式(或引用定义它的其他文档)?

从较小的无符号整数中减去较大的无符号整数是否会产生未定义的行为,为什么?

是否像下面的示例程序一样执行赋值 c=a-b 保证 c 在所有(甚至理论上)可能的符合 C++11 标准的机器架构上都是 -3为什么?

int main()
{
    unsigned long long a=2, b=5;
    signed long long c=a-b;
}

a - b 的结果定义明确,因为如果我没记错的话,C++ 保证它们的二进制补码语义。但是,生成的 underfklow 将超出 signed long long 的范围,因此将此值转换为带符号的 long long 将是未定义的行为。

无符号值的减法由 (3.9.1) [basic.fundamental]/4:

明确定义

Unsigned integers, declared unsigned, 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.46

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

但是,分配导致 c 具有实现定义的值(也就是说,您的里程可能会有所不同)。关于赋值运算符,(5.17)[expr.ass]/3不得不说

If the left operand is not of class type, the expression is implicitly converted (Clause 4) to the cv-unqualified type of the left operand.

第 4 条 ([conv]) 在 (4.7) [conv.integral]/3

中说

If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bit-field width); otherwise, the value is implementation-defined.

重申一下:a - b是明确定义的,c = a - b不是因为a - b的结果不能用c表示。

历史原因是,虽然今天几乎所有计算机都使用二进制补码表示有符号整数,但在过去,有些机器使用 other representations(特别是一个的补码和有符号的大小)来表示不具有与二进制补码相同的取值范围。如果无符号到有符号的转换是根据二进制补码表示的自然术语定义的,C++ 将不可能(或至少非常困难)在此类机器上实现,并且如果它是根据其中一种表示的自然术语定义的,我们今天会有更大的问题。