输出的差异打印预先初始化的Java double与内联

acc*_*sse 12 java string floating-point

在将构建从Java 1.6升级到1.7时,我们的单元测试开始失败,因为两个版本如何处理在双精度上打印尾随零的差异.

这个例子可以复制:

double preInit = 0.0010d;
System.out.println("pre-init: " + preInit);
System.out.println("  inline: " + 0.0010d);
Run Code Online (Sandbox Code Playgroud)

Java 1.6将输出:

pre-init: 0.0010
  inline: 0.0010
Run Code Online (Sandbox Code Playgroud)

Java 1.7将输出:

pre-init: 0.001
  inline: 0.0010
Run Code Online (Sandbox Code Playgroud)

我有两个问题:

  1. 为什么内联串联的打印与预初始化值的相同串联不同?
  2. Java 1.6和1.7之间的哪些变化导致了从版本到版本的输出差异?

acc*_*sse 3

对于第 1 部分,事实证明差异在于编译器优化代码的方式。

内联案例反编译为:

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
Run Code Online (Sandbox Code Playgroud)

操作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
Run Code Online (Sandbox Code Playgroud)

操作 11 将标签“pre-init:”压入堆栈,然后以下操作使用 StringBuilder 附加 double 值。

我认为 @PM77-1 提到的 Java bug 已在 Java Double 类中修复,但未在编译器中修复。