Narrowing conversions to a larger type (and back again)

Viewed 241

I understand that as of C++11, the conversion from int to unsigned long long is considered a narrowing conversion since unsigned long long cannot represent any negative values, despite being sufficiently large to represent all of the bits. My question is, if I force the conversion and then convert back to int, am I guaranteed to get the same value (specifically meaning that it is neither undefined nor implementation-defined behavior)?

int i = -42;
unsigned long long ull = static_cast<unsigned long long>(i);
int j = static_cast<int>(ull); // is this guaranteed to be -42?

Additionally, I would like to know about cases where only the signedness of the type is changed:

long long ll = -281474976710655LL;
unsigned long long ull = static_cast<unsigned long long>(ll);
long long n = static_cast<long long>(ull);

Are these values, j and n guaranteed to be the same as i and ll (respectively, of course)? Are there any portability or architecture (32 vs 64 bit) issues that need to be considered?

1 Answers

Both of your examples are implementation-defined.

Signed to unsigned conversions are well defined, conv.integral/2:

If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type).

Unsigned to signed conversions, however, are only well defined, if the source value can be represented in the destination type, 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.

For both of your examples, source value cannot be represented (as both unsigned values are larger than the maximum possible value of the signed number), so these conversions are implementation-defined.

Related