Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inconsistent behaviour of implicit conversion between unsigned and bigger signed types

Consider following example:

#include <stdio.h>

int main(void)
{
    unsigned char a  = 15; /* one byte */
    unsigned short b = 15; /* two bytes */
    unsigned int c   = 15; /* four bytes */

    long x = -a; /* eight bytes */
    printf("%ld\n", x);

    x = -b;
    printf("%ld\n", x);

    x = -c;
    printf("%ld\n", x);

    return 0;
}

To compile I am using GCC 4.4.7 (and it gave me no warnings):

gcc -g -std=c99 -pedantic-errors -Wall -W check.c

My result is:

-15
-15
4294967281

The question is why both unsigned char and unsigned short values are "propagated" correctly to (signed) long, while unsigned int is not ? Is there any reference or rule on this ?

Here are results from gdb (words are in little-endian order) accordingly:

(gdb) x/2w &x
0x7fffffffe168: 11111111111111111111111111110001    11111111111111111111111111111111 

(gdb) x/2w &x
0x7fffffffe168: 11111111111111111111111111110001    00000000000000000000000000000000
like image 788
Grzegorz Szpetkowski Avatar asked Jun 02 '14 12:06

Grzegorz Szpetkowski


2 Answers

This is due to how the integer promotions applied to the operand and the requirement that the result of unary minus have the same type. This is covered in section 6.5.3.3 Unary arithmetic operators and says (emphasis mine going forward):

The result of the unary - operator is the negative of its (promoted) operand. The integer promotions are performed on the operand, and the result has the promoted type.

and integer promotion which is covered in the draft c99 standard section 6.3 Conversions and says:

if an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.48) All other types are unchanged by the integer promotions.

In the first two cases, the promotion will be to int and the result will be int. In the case of unsigned int no promotion is required but the result will require a conversion back to unsigned int.

The -15 is converted to unsigned int using the rules set out in section 6.3.1.3 Signed and unsigned integers which says:

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.49)

So we end up with -15 + (UMAX + 1) which results in UMAX - 14 which results in a large unsigned value. This is sometimes why you will see code use -1 converted to to an unsigned value to obtain the max unsigned value of a type since it will always end up being -1 + UMAX + 1 which is UMAX.

like image 181
Shafik Yaghmour Avatar answered Sep 30 '22 09:09

Shafik Yaghmour


int is special. Everything smaller than int gets promoted to int in arithmetic operations.

Thus -a and -b are applications of unary minus to int values of 15, which just work and produce -15. This value is then converted to long.

-c is different. c is not promoted to an int as it is not smaller than int. The result of unary minus applied to an unsigned int value of k is again an unsigned int, computed as 2N-k (N is the number of bits).

Now this unsigned int value is converted to long normally.

like image 35
n. 1.8e9-where's-my-share m. Avatar answered Sep 30 '22 09:09

n. 1.8e9-where's-my-share m.