Reputation: 55
Subtracting two unsigned variables I expect an unsigned result. I do realize that overflow happens but that's ok, I'm actually counting on it. Seems like that's not the case when the result needs to be used in another operation. Is this standard or undefined behavior?
uint8_t n1 = 255;
uint8_t z = 0;
uint8_t n = 1;
printf("n1 is %" PRIu8 "\n", n1);
printf("z - n is %" PRIu8 "\n", z - n);
printf("n1 < z: %s\n", n1 < z ? "yes" : "no");
printf("z - n < z: %s\n", z - n < z ? "yes" : "no");
printf("(uint8_t)(z - n) < (uint8_t)z: %s\n", (uint8_t)(z - n) < (uint8_t)z ? "yes" : "no");
Output:
n1 is 255
z - n is 255
n1 < z: no
z - n < z: yes
(uint8_t)(z - n) < (uint8_t)z: no
Upvotes: 3
Views: 848
Reputation: 224313
In arithmetic, integers narrower than int
are promoted to int
, and then arithmetic on them is done in the int
type. If you store the result in a uint8_t
or other type, it will be converted to that type. But if you pass it to printf
, it will remain an int
.
In C, the usual arithmetic conversions for real numbers are:
long double
, the other is converted to long double
.double
, the other is converted to double
.float
, the other is converted to float
.The integer promotions are:
unsigned int
, it is not changed.int
can represent all values of the type, the value is converted to int
.unsigned int
.1 The C standard actually uses a technical classification of rank, which involves further details. It affects C implementations where multiple integer types can have the same width, aside from just being signed and unsigned.
Upvotes: 5
Reputation: 755010
When the variables are of type uint8_t
, they are both promoted to (signed) int
and then the subtraction occurs between the promoted values, yielding a (signed) int
value. It is mandated behaviour.
In C11, §6.3.1.8 Usual arithmetic conversions says:
Many operators that expect operands of arithmetic type cause conversions and yield result types in a similar way. The purpose is to determine a common real type for the operands and result. For the specified operands, each operand is converted, without change of type domain, to a type whose corresponding real type is the common real type. Unless explicitly stated otherwise, the common real type is also the corresponding real type of the result, whose type domain is the type domain of the operands if they are the same, and complex otherwise. This pattern is called the usual arithmetic conversions:
- First, if the corresponding real type of either operand is
long double
, the other operand is converted, without change of type domain, to a type whose corresponding real type islong double
.- Otherwise, if the corresponding real type of either operand is
double
, the other operand is converted, without change of type domain, to a type whose corresponding real type isdouble
.- Otherwise, if the corresponding real type of either operand is
float
, the other operand is converted, without change of type domain, to a type whose corresponding real type isfloat
.62)- Otherwise, the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands:
- If both operands have the same type, then no further conversion is needed.
- Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.
- Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
- Otherwise, if the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.
- Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.
See §6.3.1 Arithmetic operands and §6.3.1.1 Boolean, characters, and integers for more information about 'integer promotions'.
The following may be used in an expression wherever an
int
orunsigned int
may be used:
- An object or expression with an integer type (other than
int
orunsigned int
) whose integer conversion rank is less than or equal to the rank ofint
andunsigned int
.- A bit-field of type
_Bool
,int
,signed int
, orunsigned int
.If an
int
can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to anint
; otherwise, it is converted to anunsigned int
. These are called the integer promotions.58) All other types are unchanged by the integer promotions.
The term 'rank' is defined in that section; it's complex, but basically, long
has a higher rank than int
, and int
has a higher rank than char
.
The rules are undoubtedly slightly different in C++, but the net result is essentially the same.
Upvotes: 6