Search code examples
c++language-lawyerstandardscompiler-warningsinteger-overflow

What is the standard rule on arithmetic operations when storing temporary values to a certain data type?


I had some code which I did a long time ago (I was using visual studio 2003 back then). Now I'm using gcc and some values are overflowing, I took a look at what was happening and it kind of surprises me.

Let me show what is going on:

Works (Output = 85):

int b = 35000000;
unsigned long a = 30000000;
unsigned long n = ( 100 * a ) / b;

Doesn't works (Overflow):

int b = 35000000;
int a = 30000000;
unsigned long n = ( 100 * a ) / b;

Doesn't works (Overflow):

int b = 35000000;
unsigned long n = ( 100 * 30000000 ) / b;

This should all be correct. Now what bugs me is that:

unsigned long b= 35000000;
unsigned long n = ( 100 * 30000000 ) / b;

Used to work! Now it doesn't.

Well, actually it still works with microsoft compilers, but it doesn't work with clang and gcc. Go ahead and compile it with different compilers if you'd like: http://rextester.com/BZU89042

  • Output = 85 - Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x86
  • Output = 527049830640 - g++ 5.4.0
  • Output = 527049830640 - clang 3.8.0

What is the standard C++ rule about these?


Solution

  • The rule for determining the type of an integer literal is, from [lex.icon]:

    The type of an integer literal is the first of the corresponding list in Table 7 in which its value can be represented.

    Where, with no suffix, the list of types is int then long int then long long int. After that, when we do math, the rule is always "the usual arithmetic conversions,", which are enumerated in [expr]:

    This pattern is called the usual arithmetic conversions, which are defined as follows:

    (11.1) If either operand is of scoped enumeration type, no conversions are performed; if the other operand does not have the same type, the expression is ill-formed.

    (11.2) If either operand is of type long double, the other shall be converted to long double.

    (11.3) Otherwise, if either operand is double, the other shall be converted to double.

    (11.4) Otherwise, if either operand is float, the other shall be converted to float.

    (11.5) Otherwise, the integral promotions shall be performed on both operands.63 Then the following rules shall be applied to the promoted operands:

    (11.5.1) If both operands have the same type, no further conversion is needed.

    (11.5.2) Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank shall be converted to the type of the operand with greater rank.

    (11.5.3) Otherwise, if the operand that has unsigned integer type has rank greater than or equal to the rank of the type of the other operand, the operand with signed integer type shall be converted to the type of the operand with unsigned integer type.

    (11.5.4) 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, the operand with unsigned integer type shall be converted to the type of the operand with signed integer type.

    (11.5.5) Otherwise, both operands shall be converted to the unsigned integer type corresponding to the type of the operand with signed integer type.


    Let's go through your examples:

    int b = 35000000;
    unsigned long a = 30000000;
    unsigned long n = ( 100 * a ) / b;
    

    This is fine because the type of 100 * a is an unsigned long (because of 11.5.3), which is wide enough to hold that result.

    The rest don't work because the multiplication yields a type int. Two ints multiplied together yields an int (because of 11.5.1), and in the first case we explicitly declare a to be int and in the rest of the cases the literal 30000000 has type int (because it is small enough to be represented by int)

    Note that in this last example:

    unsigned long b= 35000000;
    unsigned long n = ( 100 * 30000000 ) / b;
    

    It doesn't matter that b or n are declared unsigned long, the expression (100 * 30000000) is still two ints multiplied together, and so has type int, regardless of representation. Both gcc and clang warn about this overflow.

    To fix it, you can always add suffixes to your literals. In this case, either 100u or 30000000u would do the trick. This makes the literal of type unsigned int (per [lex.icon]), which makes the type of the multiplication unsigned int (per [expr]/11.5.3), which would not overflow.