简体   繁体   中英

Arithmetic operations on unsigned variables produce signed values, is it standard behavior?

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

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 is long 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 is double .
  • 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 is float . 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 or unsigned int may be used:

  • An object or expression with an integer type (other than int or unsigned int ) whose integer conversion rank is less than or equal to the rank of int and unsigned int .
  • A bit-field of type _Bool , int , signed int , or unsigned 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 an int ; otherwise, it is converted to an unsigned 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.

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:

  • If either type is long double , the other is converted to long double .
  • Otherwise, if either is double , the other is converted to double .
  • Otherwise, if either is float , the other is converted to float .
  • Otherwise, the integer promotions are performed on each operand. Then:
    • If both operands have the same time, no further conversion is performed.
    • Otherwise, if both are signed or both are unsigned, the narrower 1 operand is converted to the wider operand.
    • Otherwise, if the unsigned operand is as wide as or wider than the other, the signed operand is converted to the unsigned type.
    • Otherwise, if the signed type can represent all the values of the unsigned type, the unsigned operand is converted to the signed type.
    • Otherwise, both operands are converted to the unsigned type corresponding to the signed type.

The integer promotions are:

  • If a type is wider 1 than unsigned int , it is not changed.
  • Otherwise, if an int can represent all values of the type, the value is converted to int .
  • Otherwise, the value is converted to unsigned int .

Footnote

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM