Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Implicit Conversion (Signed + Unsigned)

I understand that, regarding implicit conversions, if we have an unsigned type operand and a signed type operand, and the type of the unsigned operand is the same as (or larger) than the type of the signed operand, the signed operand will be converted to unsigned.

So:

unsigned int u = 10;  
signed int s = -8;

std::cout << s + u << std::endl;

//prints 2 because it will convert `s` to `unsigned int`, now `s` has the value
//4294967288, then it will add `u` to it, which is an out-of-range value, so,
//in my machine, `4294967298 % 4294967296 = 2`

What I don't understand - I read that if the signed operand has a larger type than the unsigned operand:

  • if all values in the unsigned type fit in the larger type then the unsigned operand is converted to the signed type

  • if the values in the unsigned type don't fit in the larger type, then the signed operand will be converted to the unsigned type

so in the following code:

signed long long s = -8;
unsigned int u = 10;
std::cout << s + u << std::endl;

u will be converted to signed long long because int values can fit in signed long long??

If that's the case, in what scenario the smaller type values won't fit in the larger one?

like image 688
2013Asker Avatar asked Jul 24 '13 11:07

2013Asker


People also ask

How does C convert signed to unsigned?

To convert a signed integer to an unsigned integer, or to convert an unsigned integer to a signed integer you need only use a cast. For example: int a = 6; unsigned int b; int c; b = (unsigned int)a; c = (int)b; Actually in many cases you can dispense with the cast.

Does C allow implicit conversion?

Implicit Type Conversion is also known as 'automatic type conversion'. It is done by the compiler on its own, without any external trigger from the user. It generally takes place when in an expression more than one data type is present.

What happens in implicit conversion?

An implicit conversion sequence is the sequence of conversions required to convert an argument in a function call to the type of the corresponding parameter in a function declaration. The compiler tries to determine an implicit conversion sequence for each argument.

What is implicit type promotion?

Whenever a small integer type is used in an expression, it is implicitly converted to int which is always signed. This is known as the integer promotions or the integer promotion rule.


2 Answers

Relevant quote from the Standard:

5 Expressions [expr]

10 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, which are defined as follows:

[2 clauses about equal types or types of equal sign omitted]

— Otherwise, if the operand that has unsigned integer type has rank greater than or equal to the rank of the type of the other operand, the operand with signed integer type shall be 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, the operand with unsigned integer type shall be converted to the type of the operand with signed integer type.

— Otherwise, both operands shall be converted to the unsigned integer type corresponding to the type of the operand with signed integer type.

Let's consider the following 3 example cases for each of the 3 above clauses on a system where sizeof(int) < sizeof(long) == sizeof(long long) (easily adaptable to other cases)

#include <iostream>

signed int s1 = -4;
unsigned int u1 = 2;

signed long int s2 = -4;
unsigned int u2 = 2;

signed long long int s3 = -4;
unsigned long int u3 = 2;

int main()
{
    std::cout << (s1 + u1) << "\n"; // 4294967294
    std::cout << (s2 + u2) << "\n"; // -2 
    std::cout << (s3 + u3) << "\n"; // 18446744073709551614  
}

Live example with output.

First clause: types of equal rank, so the signed int operand is converted to unsigned int. This entails a value-transformation which (using two's complement) gives te printed value.

Second clause: signed type has higher rank, and (on this platform!) can represent all values of the unsigned type, so unsigned operand is converted to signed type, and you get -2

Third clause: signed type again has higher rank, but (on this platform!) cannot represent all values of the unsigned type, so both operands are converted to unsigned long long, and after the value-transformation on the signed operand, you get the printed value.

Note that when the unsigned operand would be large enough (e.g. 6 in these examples), then the end result would give 2 for all 3 examples because of unsigned integer overflow.

(Added) Note that you get even more unexpected results when you do comparisons on these types. Lets consider the above example 1 with <:

#include <iostream>

signed int s1 = -4;
unsigned int u1 = 2;
int main()
{
    std::cout << (s1 < u1 ? "s1 < u1" : "s1 !< u1") << "\n";  // "s1 !< u1"
    std::cout << (-4 < 2u ? "-4 < 2u" : "-4 !< 2u") << "\n";  // "-4 !< 2u"
}

Since 2u is made unsigned explicitly by the u suffix the same rules apply. And the result is probably not what you expect when comparing -4 < 2 when writing in C++ -4 < 2u...

like image 93
TemplateRex Avatar answered Sep 19 '22 15:09

TemplateRex


signed int does not fit into unsigned long long. So you will have this conversion: signed int -> unsigned long long.

like image 43
sasha.sochka Avatar answered Sep 21 '22 15:09

sasha.sochka