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?
So it appears that the result of
numberA + 1
was promoted touint32_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.