Why isn’t implicit type conversion happening from unsigned short int to signed char?

85 Views Asked by At

code 1:

#include <stdio.h>

int main()
{
    short int a = -1; // ----> Line 1
    unsigned int b = 122; // ----> Line 2
    
    if (a > b)
        printf("a is big");
    else
        printf("a is small");

    return 0;
}

Output: a is big

Since here the Line 1 is implicitly converted to Line 2, ie: signed short int to unsigned int.

similarly

code 2:

#include <stdio.h>

int main()
{
    char a = -1; // ---> Line 3
    unsigned short int b = 122; // ---> Line 4
    
    if (a > b)
        printf("a is big");
    else
        printf("a is small");

    return 0;
}

Output: a is small

Here in code 2, since unsigned short int (Line 4) is greater than signed char (Line 3), why is the implicit conversion not happening?

or

Why doesn't the implicit conversion for unsigned short int and signed char take place?

2

There are 2 best solutions below

0
Eric Postpischil On

char, signed char, unsigned char, short, and unsigned short are never considered as result types of the integer promotions.

The integer promotions originated in early C development where arithmetic was largely done using the native processor registers in their ordinary widths, and char and short were types used for storing data. The int type was used as the “natural” type on the processor, matching its registers. So char and short data was loaded into processor registers and then operated on with, in effect, instructions that performed int arithmetic. The current rules of standard C reflect this history; the integer promotions bring integer types up to at least int or unsigned int.

In your C implementation, unsigned short int is narrower than int, so it is promoted to int. So is char. Thus the char value −1 is promoted to int, yielding an int value of −1, and the unsigned short int value of 122 is promoted to int, yielding an int value of 122, and these are compared. Then −1 is not greater than 122, so a > b evaluates as false.

The specific rules for the usual arithmetic conversions are:

  • If either operand (or, if complex, its corresponding real type) is long double, the other operand is converted to long double.

  • Otherwise, if either operand (or, if complex, its corresponding real type) is double, the other operand is converted to double.

  • Otherwise, if either operand (or, if complex, its corresponding real type) is float, the other operand is converted to float.

  • Otherwise, the integer promotions are performed on both operands: A value with a type with integer conversion rank less than int is converted to int if int can represent all values of the source type (for example, if unsigned short is the same width as int, it has some values that int cannot represent) or to unsigned int otherwise. (Values with a type with greater rank are not converted.) Values with bit-field types of _Bool, int or unsigned int are also converted in this way.

  • If both resulting operands have the same type, no further conversion is performed.

  • Otherwise, if both have signed type or both have unsigned type, the operand with lower rank is converted to the other type.

  • Otherwise, if rank of the unsigned type is at least the rank of the signed type, the signed type is converted to the unsigned type.

  • Otherwise, if the signed type can represent all the values of the unsigned type, the unsigned type is converted to the signed type.

  • Otherwise, both operands are converted to the unsigned type corresponding to the signed type. (For example, if the operands are unsigned short and int, and those have the same width, they will be converted to unsigned int.)

0
chqrlie On

This little riddle hides a difficult problem: integer promotions may behave differently on different systems, depending on the size of types char, short and int.

When an operation is evaluated between types that have a rank lesser or equal to that of int, integer promotions are performed and the operation is evaluated on the resulting type, int or unsigned int depending on the original types.

If both types are smaller (have fewer value bits) than int, they are promoted to int, which preserves the value. If one or both have the same number of value bits as unsigned int, they are both promoted to unsigned int, which changes negative values to large positive ones.

In addition to this difficulty, you are storing the value -1 to a variable of type char. Depending on the signedness of type char, the value -1 may be converted to the positive value UCHAR_MAX. This is the case if type char is unsigned.

Let's investigate your examples:

code 1:

short int a = -1; // ----> Line 1
unsigned int b = 122; // ----> Line 2

short int a is promoted to unsigned int, the type of b and the value -1 changes to UINT_MAX, which is much larger than 122, hence the comparison is true, a is big. This behavior does not depend on the architecture.

code 2:

char a = -1; // ---> Line 3
unsigned short int b = 122; // ---> Line 4
  • if char is signed, a has the value -1, but if char is unsigned, a would have the value UCHAR_MAX, which is 255 on architectures with 8-bit bytes.

  • if type unsigned short int has the same size as unsigned int (16-bit architectures), both values are promoted to unsigned int (-1 becomes UINT_MAX) and the comparison is either UINT_MAX > 122 or 255 > 122, which is true in both cases.

  • if type unsigned short int is smaller than int, both values are promoted to int and the comparison is either -1 > 122 or 255 > 122, which depends on the signedness of type char.

Your system outputs a is small, which means char is signed and short has fewer bits than int, so the comparison is -1 > 122, which is false.