15.6.3 Parentheses and Precedence Respected

That is too weighty a subject to be discussed parenthetically . . .
--John Stuart Mill, On Liberty (1869), Chapter IV

Java implementations must respect the order of evaluation as indicated explicitly by parentheses and implicitly by operator precedence. An implementation may not take advantage of algebraic identities such as the associative law to rewrite expressions into a more convenient computational order unless it can be proven that the replacement expression is equivalent in value and in its observable side effects, even in the presence of multiple threads of execution (using the thread execution model in §17), for all possible computational values that might be involved.

In the case of floating-point calculations, this rule applies also for infinity and not-a-number (NaN) values. For example, !(x<y) may not be rewritten as x>=y, because these expressions have different values if either x or y is NaN.

Specifically, floating-point calculations that appear to be mathematically associative are unlikely to be computationally associative. Such computations must not be naively reordered. For example, it is not correct for a Java compiler to rewrite 4.0*x*0.5 as 2.0*x; while roundoff happens not to be an issue here, there are large values of x for which the first expression produces infinity (because of overflow) but the second expression produces a finite result.

So, for example, the test program:


class Test {

	public static void main(String[] args) {
		double d = 8e+307;
		System.out.println(4.0 * d * 0.5);
		System.out.println(2.0 * d);
	}
}

prints:


Infinity
1.6e+308

because the first expression overflows and the second does not.

In contrast, integer addition and multiplication are provably associative in Java; for example a+b+c, where a, b, and c are local variables (this simplifying assumption avoids issues involving multiple threads and volatile variables), will always produce the same answer whether evaluated as (a+b)+c or a+(b+c); if the expression b+c occurs nearby in the code, a smart compiler may be able to use this common subexpression.