Search code examples
cinteger-promotion

Why are integer types promoted during addition in C?


So we had a field issue, and after days of debugging, narrowed down the problem to this particular bit of code, where the processing in a while loop wasn't happening :

// heavily redacted code
// numberA and numberB are both of uint16_t
// Important stuff happens in that while loop

while ( numberA + 1 == numberB )
{
    // some processing
}

This had run fine, until we hit the uint16 limit of 65535. Another bunch of print statements later, we discovered that numberA + 1 had a value of 65536, while numberB wrapped back to 0. This failed the check and no processing was done.

This got me curious, so I put together a quick C program (compiled with GCC 4.9.2) to check this:

#include <stdio.h>
#include <stdint.h>

int main()
{

    uint16_t numberA, numberB;
    numberA = 65535;
    numberB = numberA + 1;

    uint32_t numberC, numberD;
    numberC = 4294967295;
    numberD = numberC + 1;

    printf("numberA = %d\n", numberA + 1);
    printf("numberB = %d\n", numberB);

    printf("numberC = %d\n", numberC + 1);
    printf("numberD = %d\n", numberD);

    return 0;
}

And the result was :

numberA = 65536
numberB = 0
numberC = 0
numberD = 0

So it appears that the result of numberA + 1 was promoted to uint32_t. Is this intended by the C language ? Or is this some compiler / hardware oddity?


Solution

  • So it appears that the result of numberA + 1 was promoted to uint32_t

    The operands of the addition were promoted to int before the addition took place, and the result of the addition is of the same type as the effective operands (int).

    Indeed, if int is 32-bit wide on your compilation platform (meaning that the type that represents uint16_t has lower “conversion rank” than int), then numberA + 1 is computed as an int addition between 1 and a promoted numberA as part of the integer promotion rules, 6.3.1.1:2 in the C11 standard:

    The following may be used in an expression wherever an int or unsigned int may be used: […] An object or expression with an integer type (other than int or unsigned int) whose integer conversion rank is less than or equal to the rank of int and unsigned int.

    […]

    If an int can represent all values of the original type […], the value is converted to an int

    In your case, unsigned short which is in all likelihood what uint16_t is defined as on your platform, has all its values representable as elements of int, so the unsigned short value numberA gets promoted to int when it occurs in an arithmetic operation.