Lightness Races in Orbit - 1 month ago 17

C++ Question

On x86_64 CentOS 7 GCC 4.8.5 C++11:

`#include <iostream>`

int main()

{

std::cout << ((ssize_t)1 - (size_t)5) << '\n';

}

// Output: 18446744073709551612

But:

`#include <iostream>`

int main()

{

std::cout << ((ssize_t)1 - (unsigned int)5) << '\n';

}

// Output: -4

And on i686 CentOS 6 GCC 4.8.2 C++11, they both give

`4294967292`

`#include <iostream>`

int main()

{

std::cout << ((ssize_t)1 - (ssize_t)5) << '\n';

}

// Output: -4

An extremely contrived example, obviously, and I understand that I'm hitting various clauses in the integral promotion rules depending on the platform/implementation-defined type equivalences, but on a Thursday my brain can't unravel them for a rigourous assessment.

What exactly is the sequence of standard rules that leads me to these results?

Answer

Assuming that `ssize_t`

and `size_t`

have equal rank, in your first case, [expr]/(11.5.5) applies:

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

1 will be converted to the unsigned version of `ssize_t`

, which should be `size_t`

—hence the unsigned underflow, and the value of 2^{sizeof(size_t)*8}-4.

For your second case, assuming that the rank of `unsigned`

is less than that of `ssize_t`

, and the latter can hold all of the former's values; see [expr]/(11.5.4):

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.

I.e. `5`

will be converted to `ssize_t`

, and so we get the negative result. If `ssize_t`

is not of greater rank than `unsigned`

, we'd get 2^{sizeof(unsigned)*8}-4; if instead `ssize_t`

could not hold all of `unsigned`

's values, we get the negative result again, because we fall through to the aforementioned (11.5.5).