相关问题是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
(与Java 17的发布版本一起)
和
17.0.2: 39.739999999999995
与backport of the fix的版本相符
Generally, the contract of the method says that the result does not have to match the result of just adding the values 和 dividing by the size. There’s the implementation’s freedom to provide an error correction but it’s also important to keep in mind that floating point addition is not strictly associative but we have to treat it as associative to be able to support parallel processing.
我们甚至可以证实,这一变化是一种改进:
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)
.然后,误差之间的差异会稍微小一些,然而,新算法更接近两者的正确值.