Bits of Java – Episode 5: Numeric Promotion

This week we will talk about numeric promotion. With this term we identify the process for which the compiler promotes a numeric type to a different one.

As you probably already know, this can happen using casting. On the other hand, there are some cases in which the compiler automatically performs numeric promotion, without requiring an explicit casting from your side.

These are the cases in which I would like to focus in this post. Be aware that there could probably be more of such cases with respect to the ones I am going to describe here (I am still studying, so this is not aimed to be an exhaustive discussion on the topic).

That said, let’s see two of the cases in which numeric promotion is automatically performed by the compiler.

  • At initialization: to understand this behavior we have first to remind that a numeric literal is by default assumed to be an int by Java, and a floating literal is by default assumed to be a double. These are the reasons why the first two initializations of the following example will not compile.

    /*
    * DOES NOT COMPILE, bacause the numeric literal is assumed to be
    * an int, but its value will not fit inside the int range
    */
    long numeric_literal = 6475977291;
      
    /*
    * DOES NOT COMPILE, bacause the numeric literal is assumed to be
    * a double and we want to assign it to a float
    */
    float floating_literal = 3.2;
      
    /*
    * This will work because we are explicitly saying we want a long
    * with the "L" at the end
    */
    long numeric_literal_ok = 6475977291 * 1L;
      
    /*
    * This will work because we are explicitly saying we want a float
    * with the "f" at the end
    */
    float floating_literal = 3.2f;
      
    

    Keeping that in mind, when you initialize a numerical variable with a literal that would fit in the range of its default type, but you ask that variable to be of a wider type, the compiler automatically promotes it to the wider type.

    double number = 3;
    /*
    * This will print 3.0 because the variable is a double, so
    * its value has been promoted to that type
    */
    System.out.println(number);
    

    Note that this also works in the other direction, providing that the value fits in the range of the desired type.

    /*
    * The literal 3 is automatically interpreted as int and it would fit in an int.
    * It would also fit in a short, thus the promotion to a short is done
    * automatically without casting
    */
    short number = 3;
      
    /*
    * DOES NOT COMPILE, because the literal would fit an int, but not a short,
    * so here the casting is necessary for the line to compile
    */
    short too_large = 1_000_000;
    
  • Numerical expressions: when we execute a mathematical expressions and the operands are of different numeric types, the smaller ones are automatically promoted to the type of the largest operand

    int number = 3;
    double another_number = 2.5;
    /*
    * In this case the variable number is automatically promoted to a double
    * because the other operand is a double. The result is expected to be a
    * double as well.
    */
    double result = number + another_number;
      
    /*
    * DOES NOT COMPILE, because the result is expected to be a double.
    */
    int result2 = number + another_number;
      
    /*
    * This is OK, because, even if the result of the expression is expected
    * to be a double, we are explicitely cast it to be an int
    */
    int result3 = (int) (number + another_number);
    

    Note that the result of the expression is also expected to be of the type of the operand with the wider type (double in our example), meaning that if you want the result to be of a narrower type you have to explicitly cast it, otherwise the compiler will give you an error.

    Another interesting thing to remember is that byte,short and char are automatically promoted to int, even if there is no operand of type int in the expression.

    short number = 3;
    short another_number = 4;
      
    /*
    * DOES NOT COMPILE, because the two short variables are automatically
    * promoted to int, and so the result is expected to be an int
    */
    short result = number + another_number;
      
    /*
    * This is OK because the result is of the expected type
    */
    int result1 = number + another_number;
      
    /*
    * This is also OK because we are explicitly casting
    */
    short result2 = (short) (number + another_number);
    

I think the concept of numeric promotion is one of those things that are very likely to pass overlooked when developing inside an IDE. Now I finally know why we have to cast and where instead the compiler is taking care of that (and why)!

The topic for next week will be logical operators and their short-circuit version.

by Ilenia Salvadori