Groovy基元双算法

Groovy基元双算法,groovy,primitive-types,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 bug!这怎么可能呢?这实际上与BigDecimals无关,而是与从基元整数到基元双精度的类型强制有关。这个问题是由Groovy编译器和它产生的(很可能)不正确的字节码引

这将产生127个

double middle = 255 / 2
而这个收益率为127.5%

Double middle = 255 / 2
同时,这也产生了127.5

double middle = (255 / 2) as double

我知道Groovy默认使用BigDecimal操作,但对我来说这是一个Huuge bug!这怎么可能呢?

这实际上与
BigDecimals
无关,而是与从基元整数到基元双精度的类型强制有关。这个问题是由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版本中存在此问题。在Groovy JIRA项目中。感谢您发现并报告问题

更新:Java也这样做 这似乎不是一个Groovy bug——Java也是这样做的。在Java中,您可以将两个整数的除法结果存储在double变量中,但除了转换为double的整数之外,您将什么也得不到。对于{{Double}}类型,语法不同,但字节码非常相似。对于{Double},您需要显式地将方程的至少一部分强制转换为{Double}类型,这将导致字节码将两个整数强制转换为{Double}。在java中考虑以下示例:

final类IntDivEx{
静态双分区(整数a、整数b){
返回a/b;
}
静态双分区2(整数a,整数b){
返回a/(双)b;
}
公共静态void main(字符串[]args){
系统输出打印LN(第(255,2)部分);
系统输出打印LN(第2部分(255,2));
}
}
当您运行它时,您会得到:

127.0
127.5 
现在,如果您查看它创建的字节码,您将看到如下内容:

//
//IntelliJ IDEA从.class文件重新创建的源代码
//(由Fernflower反编译器提供动力)
//
最终类IntDivEx{
IntDivEx(){
}
静态双分区(整数a、整数b){
返回(双倍)(a/b);
}
静态双分区2(整数a,整数b){
返回(双)a/(双)b;
}
公共静态void main(字符串[]args){
系统输出打印LN(第(255,2)部分);
系统输出打印LN(第2部分(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
时,需要使用强制转换