Разница в выходной печати - предварительная инициализация Java double vs. inline

При обновлении сборки с 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

У меня есть 2 вопроса:

  • Почему печать встроенного конкатенации отличается от той же конкатенации с предварительно инициализированным значением?
  • Какое изменение между Java 1.6 и 1.7 вызывает разницу в выходе с версии на версию?

Ответ 1

Для части 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

Операция 3 указывает, что она уже подталкивает константу String "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 для добавления двойного значения.

Я думаю, что ошибка Java, упомянутая в @PM77-1, была исправлена ​​в классе Java Double, но не в компиляторе.