JEP 306,在 Java 17 中实现,提供始终严格的浮点语义,弃用 strictfp
标志。这是否意味着
java.lang.Math
可以指望与
StrictMath
中的类似方法完全相同(即
java.lang.Math
方法不能再被 JVM 替换为过去允许的内在函数)?这是否也意味着无论使用哪个库,不同架构之间的浮点数学结果不应再有任何差异?我很好奇我是否误解了新的 Java 17 功能,因为我们确实看到 Apple Silicon 与 Intel 之间今天的代码存在差异。
strictfp
不是一个标志,这是一个Java关键字,确保在对浮点变量(
float
和
double
)进行运算时在每个平台上具有相同的计算结果。就您的问题而言,OpenJDK 中有两个主要 PR 涉及 JEP 306 的更改
如果您查看第一个,尤其是
StrictMath,您会发现它现在直接委托给 j.l.Math
,并且有关
strictfp
语义的注释现已删除(以及 OpenJDK 代码库中的
strictfp
关键字)。 此外,从 Java 17 开始
javac
对
strictfp
关键字发出明确的警告(参见
compiler.properties
:
从版本 17 开始,所以对于这个问题所有浮点表达式都经过严格计算,并且
strictfp
不是必需的
这是否意味着 java.lang.Math 的行为可以与 StrictMath 中的类似方法完全相同对于某些方法,例如,答案是“是”
Math.sinh(double)
委托给
StrictMath.sinh(double)
(然后再委托给
FdLibm.Sinh.compute(dobule)
),例如方法
StrictMath.toRadians(double)
委托给
Math.toRadians(double)
。最终,对于浮点变量,无论您调用
Math
还是
StrictMath
的方法并不重要,相同的代码都会在后台执行。截至同一问题的这一部分:
也就是说,java.lang.Math 方法不能再被 JVM 替换为以前允许的内部函数PR 不会对特定于平台并由 JVM 在运行时应用的内在函数强加任何更改,无论操作是否严格。而且,正如
@Holger指出的那样,严格性和内在性之间并不矛盾。
这是否也意味着无论使用哪个库,不同架构之间的浮点数学结果不应再有任何差异?答案是“从 Java 17 或更高版本开始是的”。在此之前,结果可能会有所不同,请参阅
https://stackoverflow.com/a/22335100/12473843。在 Java 17 之前,如果对浮点变量的操作不严格,标准会假设
在单独使用浮点值集或双精度值集可能导致上溢或下溢的情况下,计算可能会产生“正确答案”。 但目前语义总是严格的,您在所有平台上都会得到相同的结果。另请参阅
8268224:清理核心库注释中对“strictfp”的引用
附注
我们确实看到 Apple Silicon 与 Intel 之间今天的代码存在差异我认为编译器为不同平台生成不同的代码是很自然的,这里的问题是您是否在上述平台上看到不同的计算结果。