提问人:greg 提问时间:12/10/2022 最后编辑:greg 更新时间:1/5/2023 访问量:117
Java 浮点计算行为差异
Java floating point computations behavior differences
问:
在用 Java 实现的科学计算管道的上下文中,我们使用了 Apache Commons math 3.6.1 中的 LeastSquare 优化器,并在 Java 8 (jdk8u322-b06)、Java 和 Java 17 之间遇到了奇怪的数值差异。
在迁移到 Java 17(目前使用 17.0.5)后,我们注意到了差异。
我可以在“简单”单元测试中重现 Java 8 和 Java 17 之间的不同行为,而无需涉及完整的计算管道,因此排除了可能来自数据加载或多线程部分的所有问题。
在 JDK 17 中,严格的浮点语义已经恢复,但与编译或解释的 Java 8 相比,结果完全不同。在这种情况下,最小二乘优化器实际上不会收敛,而在 Java 8 中会收敛。
重要的一点是,所有这些结果都可以完全重现到最后一位数字,它们在每个平台配置上都是完全可预测的。
我也,但这次在完整的管道执行中观察到,我可以在 Java 8 本身上重现数值差异,具体取决于代码是否被解释或编译(在 C2 级别或 C1 我还没有调查过)
我真的很困惑,想知道这是否是已知/记录的行为,或者这是否是 JDK 8 平台或 JDK 17 中的错误? 有没有人观察到这样的问题?
感谢您的任何提示!
编辑
我无法在此处提供简单的代码片段来重现该问题。但是我提取了代码的必要部分,以在 GitHub 存储库中重现该行为。免责声明:我不是代码的原作者。
重现步骤:
- git clone https://github.com/gjevardat/ReproduceFloatingPointBug
- 运行名为 TimeSeriesModelFitterBugTest 的单元测试 我在 eclipse 中将其作为 Junit5 测试运行。
要查看行为差异,只需在 pom.xml 中将 java.version 属性从 1.8 切换到 17。
我可以确定代码发散在 Apache 数学共享资源中的位置: 在 Apache Math commmons 3.6.1 的 LevenbergMarquardtOptimizer 类中(第 453 行)
double actRed = -1.0;
if (0.1 * currentCost < previousCost) {
double r = currentCost / previousCost;
actRed = 1.0 - r * r;
}
在迭代 17 时,actRed 变为 0,因为在 Java 17 中为 r==1,而在 Java 8 中略为正数 由于该值为 0,稍后(第 533 行)将抛出非收敛异常
if (FastMath.abs(actRed) <= TWO_EPS &&
preRed <= TWO_EPS && ratio <= 2.0) {
throw new ConvergenceException(LocalizedFormats.TOO_SMALL_COST_RELATIVE_TOLERANCE,
costRelativeTolerance);
}
答: 暂无答案
评论