Reputation: 2136
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
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.4646) 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
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