PowerGamer
PowerGamer

Reputation: 2136

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

In a program below after "integral promotions" and "usual arithmetic conversions" both operands of operator - remain unsigned long long. Afterwards, C++11 standard says:

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

Does the standard define anywhere in more detail how exactly the subtraction is performed (or refers to some other document that defines it)?

Does subtracting a larger unsigned integer from smaller unsigned integer produce an undefined behavior or not and why?

Does performing an assignment c=a-b as in the example program below guarantee that c will be -3 on ALL (even theoretically) possible machine architectures compliant with C++11 standard and why?

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

Upvotes: 1

Views: 2133

Answers (2)

Wintermute
Wintermute

Reputation: 44023

The subtraction of unsigned values is well-defined by (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.

However, the assignment causes c to have an implementation-defined value (that is to say, your mileage may vary). About assignment operators, (5.17) [expr.ass]/3 has to say

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.

And Clause 4 ([conv]) says in (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.

To reiterate: a - b is well-defined, c = a - b is not because the result of a - b is not representable by c.

The historical reason for this is that while today virtually all computers use two's complement representation for signed integers, back in the olden days there were machines that used other representations (notably one's complement and signed magnitude) that do not have the same value range as two's complement. Had unsigned-to-signed conversion been defined in terms natural for two's complement representation, C++ would have been impossible (or at least very difficult) to implement on such machines, and had it been defined in terms natural for one of those representations, we'd have a bigger problem today.

Upvotes: 6

Puppy
Puppy

Reputation: 146910

The result of a - b is well-defined as C++ guarantees two's complement semantics for them, if I recall correctly. However, the resulting underfklow would be outside the range of signed long long and therefore converting this value to a signed long long will be undefined behaviour.

Upvotes: -1

Related Questions