Groovy本机双重算术

时间:2019-04-22 05:10:59

标签: groovy primitive-types

这将产生127

double middle = 255 / 2

虽然产量为127.5

Double middle = 255 / 2

与此同时产生127.5

double middle = (255 / 2) as double

我知道Groovy默认使用BigDecimal进行操作,但是对我来说这是一个Huuge错误!怎么会这样?

1 个答案:

答案 0 :(得分:2)

这实际上与BigDecimals无关,而是与从原始整数到原始double的类型强制转换无关。该问题是由Groovy编译器及其生成的(很可能是)不正确的字节码引起的。看一下以下第一种情况的字节码表示。以下Groovy代码:

void ex1() {
    double x = 255 / 2
    println x
}

被编译为字节码,可以表示为:

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);
}

这表明在这种情况下,不可能获得127.5的结果,因为255 / 2表达式的结果存储在类型int的变量中。感觉这是行为不一致的一个示例,因为这是使用Double的方法的字节码的样子:

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);
}

该用例的主要问题是添加@TypeChecked不会阻止您犯此错误-编译通过并返回错误的结果。但是,当我们在使用@TypeChecked的方法中添加Double注释时,会引发编译错误。添加@CompileStatic可解决问题。

我已经进行了一些测试,并且可以确认最近的 2.5.6 3.0.0-alpha-4 版本中都存在此问题。 I've created a bug report在Groovy JIRA项目中。感谢您发现并报告问题!

更新:Java也做同样的事情

似乎这不是Groovy的错误-Java也是如此。在Java中,您可以在double变量中存储两个整数的除法结果,但是除了将整数强制转换为double以外,您什么都不会得到。使用{{Double}}类型在语法方面有所不同,但在字节码方面却非常相似。使用{{Double}},您需要将等式的至少一部分显式转换为{{double}}类型,这将导致将两个整数都转换为{{double}}的字节码。考虑下面的Java示例:

final class IntDivEx {

    static double div(int a, int b) {
        return a / b;
    }

    static Double div2(int a, int b) {
        return a / (double) b;
    }

    public static void main(String[] args) {
        System.out.println(div(255,2));
        System.out.println(div2(255,2));
    }
}

运行它会得到:

127.0
127.5 

现在,如果您查看它创建的字节码,您将看到类似这样的内容:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

final class IntDivEx {
    IntDivEx() {
    }

    static double div(int a, int b) {
        return (double)(a / b);
    }

    static Double div2(int a, int b) {
        return (double)a / (double)b;
    }

    public static void main(String[] args) {
        System.out.println(div(255, 2));
        System.out.println(div2(255, 2));
    }
}

Groovy与Java之间的唯一区别(就语法而言)是Groovy允许您将整数隐式转换为Double,这就是为什么

Double x = 255 / 2

是Groovy中的正确语句,而在这种情况下,Java在编译过程中失败,并出现以下错误:

Error:(10, 18) java: incompatible types: int cannot be converted to java.lang.Double

这就是为什么在Java中,当您将整数分配给Double时,需要使用强制转换。