Ranking sometimes fails when int and long have the same width in C

Viewed 76

Let platform 1 have the width of int 4 bytes and the width of long 8 bytes.

Let platform 2 have the width of int 4 bytes and the width of long be the same as the width of int.

Then given:

unsigned int x = 2;
long signed int y = 3;
func(x * y);

When ran on platform 1, the effective type of the first argument of func is long signed int. This is as expected. It follows § 6.3.1.1.1.4 that the unsigned int type has the same rank as signed int. It also then follows that signed int type has a lower rank than long signed int according to § 6.3.1.1.1.3. This then triggers the result of the multiplication to be converted to long signed int type, following § 6.3.1.8.1.4.4. Great!

When ran on platform 2, the result of the multiplication is long unsigned int. Why?


Background

Section § 6.3.1.1, subsection 1, point 3 in the C99 standard says:

The rank for long long int shall be greater than the rank of long int, which shall be greater than the rank of int, which shall be greater that the rank of short int, which shall be greater than the rank of signed char.

One thing this suggests is that long int has a higher rank than int.

Also, point 4 in the same paragraph in the C99 standard says:

The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type, if any.

A couple of things from here are that the unsigned int type has the same rank as the signed int type. Similarly, the long unsigned int type has the same rank as long signed int.

Lastly, in section § 6.3.1.8, subsection 1, point 4.3 says:

Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.

And point 4.4:

Otherwise, if the type of the operand with signed integer type can represent all values of the type of the operand with unsigned integer type, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.


Test code

#include <stdio.h>

#define func(x) _Generic((x), long unsigned int: func_longunsignedint, long signed int: func_longsignedint, signed int: func_signedint, unsigned int: func_unsignedint)(x);

void func_longunsignedint (long unsigned int x)
{
    printf("%s\t%lu\n", __func__, x);
}

void func_longsignedint (long signed int x)
{
    printf("%s\t%ld\n", __func__, x);
}

void func_signedint (signed int x)
{
    printf("%s\t%d\n", __func__, x);
}

void func_unsignedint (unsigned int x)
{
    printf("%s\t%u\n", __func__, x);
}

int main(void)
{
    printf("int width %d\n", sizeof(int));
    printf("long width %d\n", sizeof(long));
    unsigned int x = 2;
    long signed int y = -3;
    func(x * y);
}

For platform 1, compile with gcc -m64 to hopefully force long to the 8 bytes and int be 4 bytes.

For platform 2, compile with gcc -m32 to hopefully force long to the 4 bytes and int be 4 bytes.

1 Answers

We take unsigned and long signed int and follow C11 6.3.1.8:

  • 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, then the operand with unsigned integer type is converted to the type of the operand with signed integer type.

On 64-bit platform #1 type long signed int can represent all values of unsigned, because it has 64 bits vs unsigned has 32bits. So the unsigned is converted to long signed int.

On 32-bit platform #2 type long signed int can't represent all values of unsigned (UINT_MAX=2^32 but LONG_MAX=2^31-1). So we continue...

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

...so on platform #2 the "unsigned integer type corresponding to the type of signed integer type" - this is long signed int + unsigned = long unsigned int. So on platform #2 both operands are converted to long unsigned int, thus the result your are seeing.

Related