rainer
rainer

Reputation: 7099

Type of an expression

Consider the following part of a C++ program; the values printed to the console are given as comments.

{ // case 1
unsigned int x = 10; 
unsigned int y = 20; 
std::cout << "u/u x - y: " << x-y << std::endl; // 4294967286
}   

{ // case 2
int x = 10; 
int y = 20; 
std::cout << "s/s x - y: " << x-y << std::endl; // -10
}   

{ // case 3
unsigned int x = 10; 
int y = 20; 
std::cout << "u/s x - y: " << x-y << std::endl; // 4294967286
}   

{ // case 4
int x = 10; 
unsigned int y = 20; 
std::cout << "s/u x - y: " << x-y << std::endl; // 4294967286
}

I'm trying to figure out how C++ (tried with gcc 4.7.2) defines the type (more specifically, its signedness) from an expression. For cases 1, 3, and 4, the usual arithmetic conversions should promote both values to an unsigned int:

 10 = b00000000000000000000000000001010
 20 = b00000000000000000000000000010100

Then, it would do a 2's complement to get -20 and add it:

 10 = b00000000000000000000000000001010
-20 = b11111111111111111111111111101100
      b11111111111111111111111111110110

Interpret that as an unsigned integer, and you get 4294967286 -- very well.

Obviously, you get the same calculation/result for case 2; however, the usual arithmetic conversions should result in both operands being interpreted as a signed int, and also the result seems to be interpreted as a signed integer.

From this, I'm deducing, that, if the operands are signed after the usual arithmetic conversions, the result is signed. Otherwise, the result is unsigned.

So, my questions are:

  1. Is my deduction correct?
  2. Where does the standard define this? I cannot find any reference to this in the C or C++ standard.
  3. What about other operations? I suspect that +, *, etc. will work the same way, but what about shift and logical operations?

Edit: This seems related to C++11 type of (signed + unsigned)?, but the crucial part for my question seems to be missing from the accepted answer there: will the result of an expression always be the type of both operands after the usual arithmetic conversions?

Upvotes: 4

Views: 1116

Answers (2)

Conversions follow the integer conversion rank principle. In an nutshell, integral operands are handled as follows.

First, each operand smaller than int is converted to int (if all values of the original type fit), or to unsigned int if not.

After that, if the (possibly converted) operand types are:

  • exactly the same, no conversion happens.

  • of the same size, unsigned takes precedence.

  • of different size, smaller is converted to larger. If larger is unsigned, smaller is converted to unsigned as well.

This conversion turns the operands into the same type, which is the type of the result as well.

It's covered by [expr]§9 of the C++11 standard. It also relates closely to [conv] and [conv.rank]. To which operators exactly this applies is covered in the individual operators' descriptions in subchapters of [expr].

Upvotes: 7

rainer
rainer

Reputation: 7099

Ok, it's simple, I just misread the standard... From the C++ 11, §5 [expr] p9:

Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions, ...

Upvotes: 2

Related Questions