Java 浮点计算行为差异

Java floating point computations behavior differences

提问人:greg 提问时间:12/10/2022 最后编辑:greg 更新时间:1/5/2023 访问量:117

问:

在用 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 存储库中重现该行为。免责声明:我不是代码的原作者。

重现步骤:

要查看行为差异,只需在 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);
            } 
java-8 精度 jit java-17

评论

4赞 markspace 12/10/2022
可以解释错误在哪里吗?例如,你看到了什么结果,你期望看到什么结果?我意识到数组很大,但指出哪些值(至少其中一些)是错误的,可能会帮助人们自己进行调试。
2赞 Progman 12/10/2022
编辑您的问题,将您的源代码作为最小的可重复示例,可以由其他人进行测试。
0赞 greg 12/10/2022
是的,我会这样做,为此我需要对代码进行一些返工,以便我可以在最小的单元测试中重现。
0赞 greg 1/5/2023
@Progman 刚刚添加了指向可重现示例的链接。
0赞 Progman 1/5/2023
@greg 请将您的代码添加到问题本身,而不是在外部网站上。

答: 暂无答案