After spliting the calculation formula, the results are different

After splitting the calculation formula in the following codes compiled with nvfortran, why the output of JXAvg is different? One is “1st, JXAvg= -2.7067553867765114E-017”, the other One is “2nd, JXAvg= -1.1640450816864475E-017”.

JXAvg=CorifAvgQY1Avg-IFNLFAGOAreaIE4*(1 - IFNL_HDP)(E1N1SQFDX1 +E1N2SQFDX2 + E1N3SQFDX3) &
-GHAvgOAreaIE2*((PR1N1-TiPN1-Off1N1)FDX1+(PR1N2-TiPN2-Off1N2)FDX2+(PR1N3-TiPN3-Off1N3)FDX3) &
+WSXAvg-BSXAvg+MXAvg-DispXAvg-BCXAvg+Tau0QXAvg &
-SigmaQxAvg
sponge_dis_mthd &
+AvgSigmaHAbsU
sponge_dis_mthd
write(
,*) “1st, JXAvg=”,JXAvg

JXAvg_1=CorifAvgQY1Avg
JXAvg_2=IFNLFA
GOAreaIE4*(1 - IFNL_HDP)(E1N1SQFDX1 +E1N2SQFDX2 + E1N3SQFDX3)
JXAvg_3=GHAvgOAreaIE2*((PR1N1-TiPN1-Off1N1)FDX1+(PR1N2-TiPN2-Off1N2)FDX2+(PR1N3-TiPN3-Off1N3)FDX3)
JXAvg_4=WSXAvg-BSXAvg+MXAvg-DispXAvg-BCXAvg+Tau0QXAvg
JXAvg_5=SigmaQxAvg
sponge_dis_mthd
JXAvg_6=AvgSigmaHAbsU
sponge_dis_mthd
JXAvg=JXAvg_1-JXAvg_2-JXAvg_3+JXAvg_4-JXAvg_5+JXAvg_6
write(
,*) “2nd, JXAvg=”,JXAvg

Hi Victor,

It’s likely due to differing rounding error with the change in the order of operations. Given the values are very small, this will have a greater impact.

Some things you can do to help are:

  • Use double precision over single
  • Add “-Kieee” to have the compiler use strict compliance to IEEE 754
  • Disable Fuse-Multiply Math (FMA) via “-Mnofma”
  • If using “-Ofast”, disable relaxed floating-point via “-Mnofprelaxed” or reduce the opt level to -O3.

Notes:
FMA is more precise is the fused operation has less rounding error, but can give divergent results to non-FMA operations.
While “-Kieee” is more precise, it comes at a cost of performance, same with reducing optimization.

If you’re not familiar with Floating-point accuracy, I’d suggest reading the following: Floating-point arithmetic - Wikipedia

While this article is focused on GPUs, it gives good explanations for FMA as will as accuracy considerations: Floating Point and IEEE 754

Hope this helps,
Mat