Search code examples
coverflowarithmetic-expressionsinteger-promotion

How does the integer overflow work in C?


I am a bit confused about how is arithmetic calculations are handled in a fixed point environment. Consider following lines of code:

/* unsigned short is 16 bit.*/
unsigned short x = 1000;
unsigned short res;

/* Case1: The following yields correct result  in res */
res = (x*544/100);

/* Case2: The following yields wrong result in res*/
res = (x*544); /* expected overflow here */
res = res/100;

So, my question is: I can see why case 2 yields wrong result. But - What is it that the compiler does in case 1 that yields the correct result? - Isn't the arithmetic operation essentially the same thing in case 1? Except, it's split into two statements? - Can I expect a different behavior from a different compiler?


Solution

  • This is due to the usual arithmetic conversion being applied to the operands of multiplication and then to division, which causes the short to be promoted to a larger integer type for the purpose of the calculation and then converted to short on assignment.

    The draft C99 standard in section 6.5.5 Multiplicative operators says:

    The usual arithmetic conversions are performed on the operands.

    We need to also note that the integer constants, 544 and 100 will have type int, we can find the details on why in the question what are default integer values?.

    We then can go to section 6.3.1.8 Usual arithmetic conversions and we end up at the paragraph that says:

    Otherwise, the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands:

    and we end up at the following rule:

    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.

    So the result of the calculation is an int

    Using the -Wcoversion flag gcc but surprisingly not clang produces a warning:

    warning: conversion to 'short unsigned int' from 'int' may alter its value [-Wconversion]
    res = (x*544/100);
          ^
    

    This leads to what you term the correct result in the first case since all the calculations are done in as int in your second case you lose the intermediate result from the multiplication since you assign it back to res and the value is converted to a value that fits into short.