jvm Java 17与Java 8的双重表示

ippsafx7  于 6个月前  发布在  Java
关注(0)|答案(1)|浏览(75)

在两个不同的JVM(Java 8和Java 17)之间进行平均时,相同值的差异是什么原因?
这是因为浮点数吗?还是两个版本之间有什么变化?
Java 17

public class Main {
    public static void main(String[] args) {

        List<Double> amountList = List.of(27.19, 18.97, 6.44, 106.36);

        System.out.println("JAVA 17 result: " + amountList.stream().mapToDouble(x -> x).average().orElseThrow());

    }
}

字符串
结果:39.7399999999995
Java 8

public class Main {

    public static void main(String[] args) {

        List<Double> amountList = Arrays.asList(27.19, 18.97, 6.44, 106.36);

        System.out.println("JAVA 8 result: " + amountList.stream().mapToDouble(x -> x).average().orElse(0.0));
    }
}


结果:39.740000000001

dbf7pr2w

dbf7pr2w1#

相关问题为JDK-8214761: Bug in parallel Kahan summation implementation
由于在此错误报告中提到DoubleSummaryStatistics也受到影响,因此我们可以构建一个消除所有其他影响的示例:

public class Main {
    public static void main(String[] args) {
      DoubleSummaryStatistics s = new DoubleSummaryStatistics();
      s.accept(27.19);
      s.accept(18.97);
      s.accept(6.44);
      s.accept(106.36);
      System.out.println(System.getProperty("java.version")+": "+s.getAverage());
    }
}

字符串
我曾经制作过

1.8.0_162: 39.74000000000001
17: 39.74000000000001

的数据
(with Java 17的发布版本)

17.0.2: 39.739999999999995


backport of the fix的版本相匹配。
一般来说,方法的约定是,结果不一定要与仅将值相加并除以大小的结果相匹配。实现可以自由地提供错误纠正,但也必须记住,浮点加法不是严格关联的,但我们必须将其视为关联的,以便能够支持并行处理。
我们甚至可以验证这种变化是一种改进:

DoubleSummaryStatistics s = new DoubleSummaryStatistics();
s.accept(27.19);
s.accept(18.97);
s.accept(6.44);
s.accept(106.36);
double average = s.getAverage();
System.out.println(System.getProperty("java.version") + ": " + average);

BigDecimal d = new BigDecimal("27.19");
d = d.add(new BigDecimal("18.97"));
d = d.add(new BigDecimal("6.44"));
d = d.add(new BigDecimal("106.36"));

BigDecimal realAverage = d.divide(BigDecimal.valueOf(4), MathContext.UNLIMITED);
System.out.println("actual: " + realAverage
        + ", error: " + realAverage.subtract(BigDecimal.valueOf(average)).abs());


其打印,例如,

1.8.0_162: 39.74000000000001
actual: 39.74, error: 1E-14
17.0.2: 39.739999999999995
actual: 39.74, error: 5E-15

请注意,这是打印的十进制表示的错误。如果你想知道实际的double表示与正确值有多接近,你必须用new BigDecimal(average)替换BigDecimal.valueOf(average)。然后,错误之间的差异有点小,但是,新算法更接近两者的正确值。

相关问题