strictfp
根据 JLS,我知道修饰符对方法(和类)的含义:
strictfp 修饰符的作用是使方法主体内的所有浮点或双精度表达式都显式地为 FP-strict(第 15.4 节)。
在 FP-strict 表达式中,所有中间值必须是 float 值集或 double 值集的元素,这意味着所有 FP-strict 表达式的结果必须是 IEEE 754 算术对使用单双格式表示的操作数预测的结果.
在非 FP 严格的表达式中,允许实现使用扩展指数范围来表示中间结果;粗略地说,最终效果是,在独占使用浮点值集或双精度值集可能导致上溢或下溢的情况下,计算可能会产生“正确答案”。
我一直在尝试想出一种方法来获得方法中的表达式与非strictfp
方法中的表达式之间的实际差异strictfp
。我在两台笔记本电脑上试过这个,一台配备 Intel Core i3 CPU,另一台配备 Intel Core i7 CPU。而且我看不出任何区别。
许多帖子表明,不使用的本机浮点数strictfp
可能使用 80 位浮点数,并且在可能的最小 java double(最接近零)或最高可能的 64 位 java double 之上具有额外的可表示数字。
我在下面尝试了使用和不使用strictfp
修饰符的代码,它给出了完全相同的结果。
public static strictfp void withStrictFp() {
double v = Double.MAX_VALUE;
System.out.println(v * 1.0000001 / 1.0000001);
v = Double.MIN_VALUE;
System.out.println(v / 2 * 2);
}
实际上,我认为只有在将代码编译为汇编时才会出现任何差异,因此我使用-Xcomp
JVM 参数运行它。但没有区别。
我发现另一篇文章解释了如何获取 HotSpot 生成的汇编代码(OpenJDK 文档)。我正在运行我的代码java -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly
。第一个v * 1.0000001 / 1.0000001
带有修饰符的表达式 ( ) 和没有修饰符的表达式strictfp
相同,编译为:
0x000000010f10a0a9: movsd -0xb1(%rip),%xmm0 # 0x000000010f10a000
; {section_word}
0x000000010f10a0b1: mulsd -0xb1(%rip),%xmm0 # 0x000000010f10a008
; {section_word}
0x000000010f10a0b9: divsd -0xb1(%rip),%xmm0 # 0x000000010f10a010
; {section_word}
该代码中没有任何内容可以像我预期的那样将每个步骤的结果截断为 64 位。查找 和 的 文档,他们都提到这些(SSE)指令在 64 位浮点值上运行,而不是我预期的 80 位值。因此,这些指令操作的双值集已经是 IEEE 754 值集似乎是合乎逻辑的,因此拥有和不拥有它没有区别。movsd
mulsd
divsd
strictfp
我的问题是:
- 这个分析正确吗?我不经常使用英特尔组件,所以我对我的结论没有信心。
- 是否有任何(其他)现代 CPU 架构(具有 JVM)在使用和不使用
strictfp
修饰符的操作之间存在差异?