Search code examples
javaarithmetic-expressions

how does java define the result of an arithmetic expression


I am writing a parser for Java however, I am a little lost when it comes to operations on primitive types.

For example i have these expressions:

int i;
long l;
float f;
short s;
byte b;
//this is being cast from a float to an int? should this be a cast from byte?
int var1 = (int) (l * i * f * s * b);
//this is being cast from a float to an int? should this be a cast from long?
int var2 = (int) (l * (i * f * s * b));
//again casting from float to int? should this be a cast from short?
int var3 = (int) ((f) * (l) * (s));
//this seems to be a float but i expected this to be a long
int var4 = (int) ((f) * (l));

I believed that the last operation to be done would be the resulting type, however, this doesn't seem to be the case as in the above examples. (I have not listed any operations with doubles, however, it seems that a double takes precedence like float does.)

My other thinking, is that as it has to do floating point arithmetic, it is converting it to the largest (32/64)bit type so that no information is lost unless a there is a specific cast hiding the fact that it was a float/double, for example the following expression evaluates to a long.

int var1 = (l * i * (int) d * s * b);

This thinking does fall short, however, as if there is a long/float in the same expression it is somewhat likely that you will loose information if the value of the long is too large to fit into a float.


Solution

  • The two most relevant details, which you can find in the language spec, are:

    • Multiplication (and addition, subtraction, division etc) is left-associative. So:

      l * i * f * s * b
      

      is evaluated as

      (((l * i) * f) * s) * b
      
    • The operands of the multiplication (and addition, subtraction, division etc) undergo binary numeric promotion. Loosely, this means that operands are widened to be compatible with each other.

      More precisely:

      • If one operand is a double, the other is widened to double
      • Otherwise, if one operand is a float, the other is widened to float
      • Otherwise, if one operand is a long, the other is widened to long
      • Otherwise, both operands are widened to int

      The last point tells you that even when multiplying like types, say, short and short, the operands are still widened to int

    With these two points:

    • l * i is a long
    • (l * i) * f is a float
    • ((l * i) * f) * s is a float
    • (((l * i) * f) * s) * b is a float.

    So, you are casting from float to int.