当从Java 1.6升级到1.7时,我们的单元测试开始失败,因为两个版本处理双精度浮点数末尾零的方式不同。
可以用下面的例子复制:
double preInit = 0.0010d;
System.out.println("pre-init: " + preInit);
System.out.println(" inline: " + 0.0010d);
Java 1.6将输出:
pre-init: 0.0010
inline: 0.0010
Java 1.7将输出:
pre-init: 0.001
inline: 0.0010
我有两个问题:
- 为什么内联连接的打印不同于具有预初始化值的相同连接?
- Java 1.6和1.7之间的什么变化导致了不同版本的输出差异?
对于第1部分,区别在于编译器如何优化代码。
内联case反编译为:
0: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #22; //String inline: 0.0010
5: invokevirtual #24; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
操作3表明它已经将字符串常量"inline: 0.0010"压入堆栈。
与初始化前的情况比较:
0: ldc2_w #16; //double 0.0010d
3: dstore_1
4: getstatic #18; //Field java/lang/System.out:Ljava/io/PrintStream;
7: new #24; //class java/lang/StringBuilder
10: dup
11: ldc #26; //String pre-init:
13: invokespecial #28; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
16: dload_1
17: invokevirtual #31; //Method java/lang/StringBuilder.append:(D)Ljava/lang/StringBuilder;
20: invokevirtual #35; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
23: invokevirtual #39; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
26: return
操作11将标签"pre-init: "压入堆栈,然后下面的操作使用StringBuilder附加双精度值。
我认为@PM77-1提到的Java bug已经在Java Double类中修复了,但没有在编译器中修复。