根据JLS,我知道strictfp
修饰符在方法(和类)上的含义:
JLS 8.4.3.5, strictfp methods:
JLS 15.4 FP-strict expressions:
我一直在努力想出一种方法来获得strictfp
方法中的表达式与非strictfp
方法中的表达式之间的实际差异。我已经在两台笔记本电脑上进行了尝试,其中一台使用Intel Core i3 CPU,另一台使用Intel Core i7 CPU。而且我没有任何区别。
许多帖子建议不使用strictfp
的 native 浮点可能使用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参数运行它。但是没什么区别。我发现another post解释了如何获取HotSpot(OpenJDK documentation)生成的汇编代码。我正在使用
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位。
movsd
,mulsd
和divsd
的Looking up the documentation,他们都提到这些(SSE)指令对64位浮点值进行操作,而不是我期望的80位值。因此,这些指令所操作的 double 值集已经是IEEE 754值集,这似乎合乎逻辑,因此拥有strictfp
与不拥有strictfp
之间不会有任何区别。我的问题是:
最佳答案
如果用“现代”来表示处理器支持您在问题中引用的由编译器生成的SSE2指令(mulsd
,...),则答案是否定的,strictfp
没什么不同,因为指令集没有作用允许利用strictfp
的缺失。可用的指令已经很适合计算strictfp
的精确规范。换句话说,在这种现代CPU上,您始终以相同的价格获得strictfp
语义。
如果用“现代”表示历史性的387 FPU,则有可能观察到差异,即中间计算在strictfp
模式下会上溢还是下溢(差异在于它可能不会上溢,或者在下溢时保留比预期的)。
编为387的典型strictfp
计算将像 assembly 在this answer,用精心布置乘法通过两个精心挑选的权力,以使下溢行为相同,在IEEE 754 binary64。结果通过64位存储器位置的往返处理可以解决溢出问题。
在没有strictfp
的情况下进行编译的相同计算,每个基本操作将产生一条387条指令,例如,仅针对源级乘法的乘法指令fmulp
。 (在程序开始时,将387配置为使用与binary64 53位相同的有效位宽度。)