Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-9094

Coercion to primitive double from primitive integer produces different results in dynamic and static compilation

    XMLWordPrintableJSON

    Details

    • Type: Bug
    • Status: Open
    • Priority: Major
    • Resolution: Unresolved
    • Affects Version/s: 3.0.0-alpha-4, 2.5.6
    • Fix Version/s: None
    • Component/s: bytecode, Compiler
    • Labels:
      None

      Description

      Ticket created based on the following question on Stack Overflow - https://stackoverflow.com/questions/55789411/groovy-primitive-double-arithmetic

      It seems like primitive double coercion is unstable. Consider the following example:

      void ex1() {
          double x = 255 / 2
          println x
      }
      
      void ex2() {
          Double x = 255 / 2
          println x
      }
      
      void ex3() {
          def x = 255 / 2
          println x
      }
      
      void ex4() {
          println 255 / 2
      }
      
      ex1()
      ex2()
      ex3()
      ex4()
      

      It produces the following output:

      127.0
      127.5
      127.5
      127.5
      

      I checked the generated bytecode and here is what the ex1 method implementation looks like:

          public void ex1() {
              CallSite[] var1 = $getCallSiteArray();
              double x = 0.0D;
              if (BytecodeInterface8.isOrigInt() && BytecodeInterface8.isOrigD() && !__$stMC && !BytecodeInterface8.disabledStandardMetaClass()) {
                  int var5 = 255 / 2;
                  x = (double)var5;
              } else {
                  Object var4 = var1[5].call(255, 2);
                  x = DefaultTypeTransformation.doubleUnbox(var4);
              }
      
              var1[6].callCurrent(this, x);
          }
      

      And here is what ex2 method's bytecode looks like:

          public void ex2() {
              CallSite[] var1 = $getCallSiteArray();
              Double x = null;
              if (BytecodeInterface8.isOrigInt() && !__$stMC && !BytecodeInterface8.disabledStandardMetaClass()) {
                  Object var4 = var1[8].call(255, 2);
                  x = (Double)ScriptBytecodeAdapter.castToType(var4, Double.class);
              } else {
                  Object var3 = var1[7].call(255, 2);
                  x = (Double)ScriptBytecodeAdapter.castToType(var3, Double.class);
              }
      
              var1[9].callCurrent(this, x);
          }
      

      If we compile statically the first method, it will produce 127.5 output, and will generate the following bytecode:

          public void ex1() {
              double x = DefaultTypeTransformation.doubleUnbox(NumberNumberDiv.div(255, 2));
              ((qweqwe23)this).println(x);
              Object var10000 = null;
          }
      

      But the weirdest thing happens if we try to use type checking. My first impression was that this unstable coercion could be avoided if we use type checking. However, it doesn't work. If I add @TypeChecked annotation to ex1 method, it does not make any effect - the code runs and it still produces 127.0 output. But when I add @TypeChecked to ex2 method, it does not run and produces the following compilation error.

      Error:(10, 20) Groovyc: [Static type checking] - Cannot assign value of type java.math.BigDecimal to variable of type java.lang.Double
      

        Attachments

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              wololock Szymon Stępniak
            • Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated: