4

Lets say I have 3 variables: a long, an int and a short.

long  l; 
int   i;
short s;
long  lsum;

If this is a pure math, since multiplication has a commutative property, the order of these variables doesn't matter.

 l * i * s = i * s * l = s * i * l.

Let lsum be the container of the multiplication of these 3 variables.

In C, would there be a case where a particular order of these variables cause different result?

If there is a case where the order does matter, not necessarily from this example, what would that be?

dbush
  • 205,898
  • 23
  • 218
  • 273
Nguai al
  • 958
  • 5
  • 15
  • 1
    (`s*i`) is converted to int, then `(s*i) * l` to long. `(l*i)` is converted to long, then `(l*i)* s` to long also. If ever `s*i` overflows int_max, then the order can make a difference. – aka.nice May 18 '17 at 18:58
  • 1
    @aka.nice - From your comments, it looks like the type promotion happens sequentially and there can be a overflow if going from small to big. If that is the case, starting with biggest type in the beginning would be the right thing to do. – Nguai al May 18 '17 at 19:03
  • In this case the biggest type is the same type as the product. If it is not you must cast it. (Although operands smaller than `int` will be extended to `int` internally.) – Weather Vane May 18 '17 at 19:07
  • 1
    @Nguaial: There will be no overflow, but undefined behaviour. And what do you mean with "big"? There is no guarantee `int` is smaller than `long`. – too honest for this site May 18 '17 at 19:13
  • @Olaf - What i meant by `big` is that the date type has a higher max value: e.g. Max of long is higher than max of int. The examples provided below are cases of UB? – Nguai al May 18 '17 at 19:22
  • 1
    @aka.nice "(s*i) is converted to int" lacks clarity. `s` is first converted to `int`. Then the multiplication happens. – chux - Reinstate Monica May 18 '17 at 19:23
  • 2
    @Nguaial [Max of long is higher than max of int.](http://stackoverflow.com/questions/44055758/does-the-order-of-multiplication-variables-of-different-data-type-cause-differen#comment75137371_44055758) Not quite: `LONG_MAX >= INT_MAX`. That is `>=` not `>`. – chux - Reinstate Monica May 18 '17 at 19:24
  • 1
    @: Don't rely on false assumptions! There is no guarantee any of `int`, `short` `long` has a wider range than any of the others. – too honest for this site May 18 '17 at 19:26
  • @chux yes you are right, all these details explain why I did write a comment, not a whole/correct answer ;) And the most troubling issue is that an unsigned short will generally be converted to a *signed* int if it fits, which is somehow surprising for whom did not study type promotion carefully enough – aka.nice May 18 '17 at 19:38
  • @aka.nice The issue is even more _fun_ with floating point as C allows intermediate calculations to occur at various types per `FLT_EVAL_METHOD`. – chux - Reinstate Monica May 18 '17 at 21:50

2 Answers2

5

The order does matter due to integer promotions.

When applying an arithmetic operator, each of its operands is first promoted to int if its rank is less than int (such as char or short). If one of those operands then has a higher rank still (such as long), than the smaller is promoted.

From section 6.3.1 of the C standard:

2 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.

  • A bit-field of type _Bool, int, signed int, or unsigned int.

If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.

From section 6.3.1.8:

If both operands have the same type, then no further conversion is needed.

Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.

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.

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.

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

As an example (assuming sizeof(int) is 4 and sizeof(long) is 8):

int i;
short s;
long l, result;

i = 0x10000000;
s = 0x10;
l = 0x10000000;

result = s * i * l;
printf("s * i * l=%lx\n", result);
result = l * i * s;
printf("l * i * s=%lx\n", result);

Output:

s * i * l=0
l * i * s=1000000000000000

In this example, s * i is evaluated first. The value of s is promoted to int, then the two int values are multiplied. At this point an overflow occurs unvoking undefined behavior. The result is then promoted to long and multiplied by l, with the result being of type long.

In the latter case, l * i is evaluated first. The value of i is promoted to long, then the two long values are multiplied and an overflow does not occur. The result is then multiplied by s, which is first promoted to long. Again, the result does not overflow.

In a situation like this, I'd recommend casting the leftmost operand to long so that all other operands are promoted to that type. If you have parenthesized subexpressions you may need to apply a cast there as well, depending on the result you want.

dbush
  • 205,898
  • 23
  • 218
  • 273
4

Yes, see "Type conversion" and "Type promotion" on http://www.cplusplus.com/articles/DE18T05o/

unsigned a = INT_MAX;
unsigned b = INT_MAX;
unsigned long c = 255;

unsigned long r1 = a * b * c;
unsigned long r2 = c * a * b;

r1=255 r2=13835056960065503487

r1 reflects that (a*b) is done first with types as least as long as an int, and the result is of the longest operand type, which is unsigned, so the result is unsigned and that overflows.

Travis
  • 43
  • 5
  • 1
    Obviously `r2` is the right answer. So is it safe to say to always start with biggest type as the first variable in the order of multiplication? – Nguai al May 18 '17 at 19:18
  • 2
    @Nguaial the safe way is to cast the first operand to the target type. – Weather Vane May 18 '17 at 19:19
  • 1
    For safety, do what Weather Vane mentioned. If the biggest type is first, you may still never expand to a wide enough type to hold your answer. e.g. `unsigned long r0 = a * b` will have value '1' because of overflow. But if you `unsigned long r0 = (unsigned long)a * b`, you'll get the expansion and expected answer. – Travis May 18 '17 at 19:22