1

I was wondering how does Java handle the intermediate values while evaluating a mathematical expression specially when the intermediate values exceeds their storage size. For an example,

int a = 1103515245;
int x = 1013904223;
int c = 2531011;

x = (a*x + c) & 0x7FFFFFFF; //  to make sure x will remain a positive integer.

Now, my question is while computing a*x, its value exceeds integer range (even during the addition it could happen), how this is taken care by Java Compiler?

Thank you.

abhimanyue
  • 196
  • 1
  • 12
  • Fwiw, in your specific example, it doesn't matter, the end result will be the same. – Jason C Feb 21 '23 at 21:19
  • When addition or multiplication overflows, it does it silently in Java. But you can use `Math:multiplyExact(a,b)` or `Math:addExact(a,b)` which throws when overflowing. Or use BigInteger for arbitrary precision. – Valerij Dobler Feb 21 '23 at 21:39

2 Answers2

1

JLS 15.17.1:

If an integer multiplication overflows, then the result is the low-order bits of the mathematical product as represented in some sufficiently large two's-complement format.

JLS 15.18.2:

If an integer addition overflows, then the result is the low-order bits of the mathematical sum as represented in some sufficiently large two's-complement format.

This is in contrast to C, which famously does not assume a two's-complement architecture and treats overflow of signed types as undefined behavior.

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • I know 2's complement but what does sufficiently large two's-complement mean? – abhimanyue Feb 21 '23 at 21:21
  • @abhimanyue Sufficiently large = large enough to hold the value. – Jason C Feb 21 '23 at 21:23
  • Does it mean it finds the actual value of the operation and then just discard the higher bits? – abhimanyue Feb 21 '23 at 21:24
  • @abhimanyue Effectively yes. But it does the operation first, which implicitly discards the higher bits. Then it gives you the result in a form that's large enough to hold that result. So in `int a = 0x7FFFFFFF; long b = a + a;`, `b` will end up as `-2`. – Jason C Feb 21 '23 at 21:33
  • @abhimanyue It just means "if you multiply two 64bit numbers, you need to compute all 64 lower order bits" which sounds super obvious, but spec writers are pedantic people and don't want someone to cheat and say "well you only said the lower order bits, not how many, so on x86 it was faster and cheaper to just compute 32 of them" – that other guy Feb 21 '23 at 21:35
1

Each intermediate value has a type, unless you explicity cast it it will be the one that the intermediate calculation will produce so int * int will produce another int and int * float will produce another float and overflows of immediate values will happen accordingly. Such computations will essentially be deconstructed into something like

int a = 1103515245;
int x = 1013904223;
int c = 2531011;

x = a*x;
x = x + c;
x = x & 0x7FFFFFFF;

That's not really what the compiler does because it will produce JVM bytecode which operates on a stack machine but you can roughly imagine it as doing something closer to that code than to yours. There's nothing really magical about it. Overflows will happen just like they will in the decomposed operations i wrote in the code example. If you want to avoid overflows you need to cast one or both of the input values to some intermediate calculation to a larger data type like a long. Be careful here because just casting the result won't do the trick. Then it will first overflow and then cast the overflowed value.

  • All I was trying to understand that during this process the actual value is calculated and then finally it discards the higher (out of range) bits. – abhimanyue Feb 21 '23 at 21:27
  • 1
    @abhimanyue No; the value is calculated within the confines of the types involved, so the truncation is implicit. There isn't a separate post-calculation "hey I got the whole value but let's truncate this anyways just to be obnoxious" step. – Jason C Feb 21 '23 at 21:39