提问人:omar mahameed 提问时间:10/26/2023 最后编辑:Mark Rotteveelomar mahameed 更新时间:10/26/2023 访问量:345
Java 17 与 Java 8 双重表示
Java 17 vs Java 8 double representation
问:
在 2 个不同的 JVM(Java 8 和 Java 17)之间执行平均值时,相同值的差异是什么原因?
那是因为浮点吗?还是在 2 个版本之间有其他变化?
爪哇 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.7399999999999995
爪哇 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.74000000000001
答:
16赞
Holger
10/26/2023
#1
相关问题是 JDK-8214761:并行 Kahan 求和实现中的错误
由于此错误报告中也提到了它,因此我们可以构建一个消除所有其他影响的示例: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
(随 Java 17 的发行版)
和
17.0.2: 39.739999999999995
它与修复程序的向后移植版本匹配。
通常,该方法的约定说结果不必与仅将值相加并除以大小的结果匹配。实现可以自由地提供纠错,但同样重要的是要记住,浮点加法不是严格意义上的关联,但我们必须将其视为关联才能支持并行处理。
我们甚至可以验证该更改是否是一种改进:
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
BigDecimal.valueOf(average)
new BigDecimal(average)
评论
average
double