4

I recently faced a problem on a C++ code of mine making me wonder whether I had some misunderstanding of what the compiler would do with long operations... Just look at the following code:

#include <iostream>

int main() {
    int i = 1024, j = 1024, k = 1024, n = 3;
    long long l = 5;
    std::cout << i * j * k * n * l << std::endl;  // #1
    std::cout << ( i * j * k * n ) * l << std::endl; // #2
    std::cout << l * i * j * k * n << std::endl;  // #3
    return 0;
}

For me the order in which the multiplications will happen in any of these 3 lines is undefined. However, here is what I thought would happen (assuming int is 32b, long long is 64b and they both follow the IEEE rules):

  • For line #2, the parenthesis is evaluated first, using ints as intermediate results, leading to an overflow and to store -1073741824. This intermediate result is promoted to long long for the last multiplication and the printed result should therefore be -5368709120.
  • Lines #1 and #3 are "equivalent" since the order of evaluation is undefined.

Now, for lines #1 and #3 here is were I'm unsure: I thought that although the order of evaluation was undefined, the compiler would "promote" all operations to the type of the largest operand, namely long long here. Therefore, no overflow would happen in this case since all computations would be made in 64b... But this is what GCC 5.3.0 gives me for this code:

~/tmp$ g++-5 cast.cc
~/tmp$ ./a.out 
-5368709120
-5368709120
16106127360

I would have expected 16106127360 for the first result too. Since I doubt there is a compiler bug of this magnitude in GCC, I guess the bug lies between the keyboard and the chair.

Could anyone please confirm / infirm this is undefined behaviour and GCC is correct in giving me whatever it gives (since this is undefined)?

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
Gilles
  • 9,269
  • 4
  • 34
  • 53
  • 3
    the binary operator * has left-to-right associativity – PeterT Jan 15 '16 at 13:08
  • works the same way in java. doesn't cast until it gets to the long, left to right:) – eiran Jan 15 '16 at 13:19
  • Note that overflow is undefined behavior. You're probably just going to wrap but it's not necessarily required and the compiler can do all sorts of odd things, particularly with constants like you have: http://stackoverflow.com/questions/3948479/integer-overflow-and-undefined-behavior http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html – Paul Rubel Jan 15 '16 at 13:39

2 Answers2

7

GCC is correct.

  1. Associativity for multiplication is left to right. This means that all of these expression are evaluated left to right.
  2. Promotion to higher type is only between two operands of different types of single binary operator.

For example, first expression is parsed as i * j * k * n * l = ((((i * j) * k) * n) * l) and the promotion happens only when last of multiplications is computed, but at this moment left operand is already incorrect.

gudok
  • 4,029
  • 2
  • 20
  • 30
3

The Standard unambiguously defines the grouping as follows:

5.6 Multiplicative operators [expr.mul]

1 The multiplicative operators *, /, and % group left-to-right.

This means that a * b * c is evaluated as (a * b) * c. A conforming compiler has no freedom to evaluate it as a * (b * c).

TemplateRex
  • 69,038
  • 19
  • 164
  • 304