The integer promotions sections of the standards covers conversion of signed and unsigned very well. Your question is tagged C and C++, and though I could dust off both standards, I will show you the relevant portions of the C99 standard here.
Regarding promoting a like-sized signed integer to an unsigned integer where the signed integer is NOT in range of the unsigned integer (i.e. it is less than zero(0)):
C99 6.3.1.3-p2
Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
which essentially means "add (UINT_MAX+1)
".
For example, on a typical 32 bit system, UINT_MAX
is 0xFFFFFFFF
. Now suppose you want to convert the following:
int ival = -1;
unsigned int uval = (unsigned int)ival;
According to the standard, ival
will be promoted to unsigned int
by doing the following:
uval = (UINT_MAX+1) + (-1);
Which of course, results in uval = UINT_MAX;
. This is defined by the standard.
Converting to signed from unsigned is a different story. Immediately following the previous section of the standard cited above is:
C99 6.3.1.3-p3
Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.
This essentially means you cannot rely on an unsigned conversion to like-size signed result to have implementation-indepentant behavior if the value in the unsigned is not within the allowable range of the signed int. In other words this:
unsigned int uval = INT_MAX;
int val = (int)uval;
has defined behavior, since INT_MAX is a value within the signed integer range, whereas this:
unsigned int uval = INT_MAX + n;
int val = (int)uval;
for some arbitrary n
such that (INT_MAX+n
<= UINT_MAX
) is implementation defined. Therefore, don't rely on it. The particular portion of the standard that should scare you into avoiding doing this is the potential for: "an implementation-defined signal is raised". Yikes. Just don't do it.
Examples from the text above
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
int main(int argc, char *argv[])
{
int ival = -1;
unsigned int uval = (unsigned int)ival;
printf("%d : %u\n", ival, uval);
uval = INT_MAX;
ival = (int)uval;
printf("%d : %u\n", ival, uval);
uval += 1;
ival = (int)uval;
printf("%d : %u\n", ival, uval);
return 0;
}
Output Platform: Apple LLVM version 4.2 (clang-425.0.28)
-1 : 4294967295
2147483647 : 2147483647
-2147483648 : 2147483648