Java strictfp修饰符对现代CPU有影响吗?

时间:2014-03-21 15:10:05

标签: java assembly sse expression-evaluation strictfp

根据JLS,我知道方法(和类)上strictfp修饰符的含义:

JLS 8.4.3.5, strictfp methods:

  

strictfp修饰符的作用是使所有浮点数或双精度数   方法体内的表达式应该是FP-strict(第15.4节)。

JLS 15.4 FP-strict expressions:

  

在FP-strict表达式中,所有中间值必须为   浮点值集的元素或双值集,暗示   所有FP严格表达式的结果必须是那些预测的结果   IEEE 754算术对操作数的表示使用单和双   格式。

     

在非FP严格的表达式中,授予了一些余地   使用扩展指数范围表示的实现   中间结果;粗略地说,净效应是一个   计算可能会产生“正确答案”的情况   可能会导致独占使用浮点值集或双值集   溢出或下溢。

我一直试图找到一种方法来获得strictfp方法中的表达式与非strictfp方法中的表达式之间的实际差异。我在两台笔记本电脑上试过这个,一台配备英特尔酷睿i3 CPU,另一台配备英特尔酷睿i7 CPU。我无法发挥任何作用。

许多帖子都表明原生浮点(不使用strictfp)可能使用80位浮点数,并且在最小可能的java双精度(最接近于零)或高于最小值的情况下具有额外可表示的数字。最高可能的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位,就像我预期的那样。 movsdmulsddivsd的{​​{3}} Looking up the,他们都提到这些(SSE)指令在64位浮点值上运行,而不是80-像我预期的那样比特值。因此,这些指令操作的双值集已经是IEEE 754值集似乎合乎逻辑,因此strictfp与没有它之间没有区别。

我的问题是:

  1. 这个分析是否正确?我不经常使用英特尔组装,所以我对结论没有信心。
  2. 是否存在任何(其他)现代CPU架构(具有JVM),使用和不使用strictfp修饰符的操作之间存在差异?

1 个答案:

答案 0 :(得分:8)

如果“现代”是指支持您在编译器(mulsd,...)生成的问题中引用的SSE2指令的处理器,则答案为否,strictfp没有区别,因为指令集不允许利用strictfp的缺席。根据{{​​1}}的精确规格计算可用指令已经是最佳选择。换句话说,在那种现代CPU上,你会以相同的价格获得strictfp语义。

如果用“modern”表示历史387 FPU,那么如果中间计算在strictfp模式下溢出或下溢,则可能会观察到差异(不同之处在于它可能不溢出或者在下溢,保持比预期更精确的位。)

为387编译的典型strictfp计算看起来像this answer中的汇编,通过精心选择的2次幂进行良好的乘法运算,使得下溢的行为与IEEE 754 binary64相同。通过64位存储器位置往返结果,然后处理溢出。

在没有strictfp的情况下编译的相同计算将在每个基本操作中产生一条387指令,例如,仅用于源级乘法的乘法指令strictfp。 (387将被配置为在程序开头使用与binary64,53位相同的有效位宽度。)