Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this expression being unsigneded?

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 so I have to do this:

#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?

like image 920
Lightness Races in Orbit Avatar asked Dec 01 '16 13:12

Lightness Races in Orbit


1 Answers

Disclaimer: I refer to paragraph 11 of clause 5 of the latest draft N4606 of C++17. The wording I quote and cite is contained in paragraph 9 of N3337, which is virtually identical to the C++11 standard, and in that form also in the FD of C++14, so this answer applies to these standards as well.

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 2sizeof(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 2sizeof(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).

like image 98
Columbo Avatar answered Sep 28 '22 08:09

Columbo