title: Surprising numeric widening in Scala
body: <h2>Code</h2>
  
  <pre><code>object Test {
          def main(args: Array[String]): Unit = {
                  val x = 123456789L
                  val y = 1.0F
  
                  val a = x - (if (true) { x } else { y })
                  val b = (if (true) { x } else { y }) - x
          }
  }
  </code></pre>
  
  <hr>
  
  <h2>Result</h2>
  
  <p>Running this code, a is <code>-3.0: Float</code> and b is <code>0.0: Float</code>. (<code>x.toFloat</code> is <code>1.23456792E8: Float</code> because of precision loss.)</p>
  
  <hr>
  
  <h2>Scala Specification</h2>
  
  <p>According to <a href="https://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#conditional-expressions" rel="noreferrer">Conditional Expressions</a> and <a href="https://www.scala-lang.org/files/archive/spec/2.12/03-types.html#weak-conformance" rel="noreferrer">Weak Conformance</a>: the type of the <code>if</code> expression in both cases is <code>Float</code>. (<code>Float</code> is the weak least upper bound of <code>Long</code> (the type of the then-part) and <code>Float</code> (the type of the else part).)</p>
  
  <p>As a result, we have <code>Long - Float</code> in the first case, and <code>Float - Long</code> in the second case.</p>
  
  <p>In the first case, both operands are widened (?) to <code>Double</code> for the calculation and the result is narrowed to <code>Float</code> for assignment. In the second case, both operands are widened to <code>Float</code> only.</p>
  
  <p>According to <a href="https://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#numeric-widening" rel="noreferrer">Numeric Widening</a>, the expected type of <code>a</code> must be <code>Double</code> for both operands to be widened to <code>Double</code>. However, it is <code>Float</code>.</p>
  
  <p>What is going on here? Are any other rules at play here? Is this a bug in the compiler? Please explain in detail and cite supporting evidence. Thank you.</p>
  
  <hr>
  
  <h2>Bytecode</h2>
  
  <p>The observed behavior can be confirmed by looking at the generated bytecode:</p>
  
  <pre><code>$ javap -c 'Test$.class'
  Compiled from "Test.sc"
  public final class Test$ {
    public static final Test$ MODULE$;
  
    public static {};
      Code:
         0: new           #2                  // class Test$
         3: invokespecial #12                 // Method "&lt;init&gt;":()V
         6: return
  
    public void main(java.lang.String[]);
      Code:
         0: ldc2_w        #15                 // long 123456789l
         3: lstore_2
         4: fconst_1
         5: fstore        4
         7: lload_2
         8: l2d
         9: lload_2
        10: l2f
        11: f2d
        12: dsub
        13: d2f
        14: fstore        5
        16: lload_2
        17: l2f
        18: lload_2
        19: l2f
        20: fsub
        21: fstore        6
        23: return
  }
  </code></pre>
  
  <p>Contrast:</p>
  
  <ol>
  <li><code>l2d</code>, followed by <code>l2f</code> and <code>f2d</code> in the first case; and <code>l2f</code> in the second case</li>
  <li><code>dsub</code>, followed by <code>d2f</code> in the first case; and <code>fsub</code> in the second case</li>
  </ol>
  
  <hr>
  
  <h2>Credit</h2>
  
  <p><a href="https://twitter.com/aphyr/status/831200481141805056" rel="noreferrer">https://twitter.com/aphyr/status/831200481141805056</a>.</p>