You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by "Szymon Stępniak (JIRA)" <ji...@apache.org> on 2019/04/22 06:05:00 UTC

[jira] [Created] (GROOVY-9094) Coercion to primitive double from primitive integer produces different results in dynamic and static compilation

Szymon Stępniak created GROOVY-9094:
---------------------------------------

             Summary: Coercion to primitive double from primitive integer produces different results in dynamic and static compilation
                 Key: GROOVY-9094
                 URL: https://issues.apache.org/jira/browse/GROOVY-9094
             Project: Groovy
          Issue Type: Bug
          Components: bytecode, Compiler
    Affects Versions: 2.5.6, 3.0.0-alpha-4
            Reporter: Szymon Stępniak


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:

{code:groovy}
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()
{code}

It produces the following output:

{code:bash}
127.0
127.5
127.5
127.5
{code}

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

{code:java}
    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);
    }
{code}

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

{code:java}
    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);
    }
{code}

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

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

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.

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



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)