Skip to content

Less accuracy due to FMAs #1031

@ACSimon33

Description

@ACSimon33

Hi,

I think the topic of FMA instructions came up a couple of times before, but my question is what kind of accuracy loss do we consider as acceptable given that this is a reference implementation. The point where I stumbled across it was an eigenvalue decomposition of a 2x2 matrix

   A = [  4 1 ]
       [ -4 0 ]

The eigenvalues should be [2.0, 2.0]. When the compiler uses FMAs we get [1.999999988777289 2.000000011222711] instead. Using gfortran on Linux one has to enable native intrinsic (-march=native) to accomplish that, but on arm64 we get the slightly wrong results even with default compiler flags. To get the correct values one has to use -ffp-contract=off which disables FMAs.

Should we maybe give users a build option which disables FMAs?

For everyone interested here is a quick reproducer of the example above

#include "lapacke.h"
#include <stdio.h>

int main() {

  int matrix_layout = LAPACK_COL_MAJOR;
  char balanc = 'P';
  char jobvl = 'N';
  char jobvr = 'N';
  char sense = 'N';
  lapack_int n = 2;
  lapack_int lda = 2;
  lapack_int ldvl = 2;
  lapack_int ldvr = 2;

  double A[n*lda];
  A[0] = 4;
  A[1] = -4;
  A[2] = 1;
  A[3] = 0;

  double WR[n];
  double WI[n];
  double VL[n*ldvl];
  double VR[n*ldvr];

  lapack_int ilo[1];
  lapack_int ihi[1];
  double scale[n];
  double abnrm[1];
  double rconde[1];
  double rcondv[1];

  LAPACKE_dgeevx(matrix_layout, balanc, jobvl, jobvr, sense,
                 n, A, lda, WR, WI,
                 VL, ldvl, VR, ldvr,
                 ilo, ihi, scale, abnrm, rconde, rcondv);

  printf("%1.15f %1.15f", WR[0], WR[1]);

  return 0;
}

The problematic line that causes the accuracy loss is line 253 in dlanv2.f:

B = BB*CS + DD*SN

This expression should evaluate to zero but instead evaluates to -2.5189846806723163E-017 due to different rounding of the FMA instructions. This value is later compare to zero and used inside a SQRT which increases the inaccuracy to ~ 10^-9:

  IF( C.NE.ZERO ) THEN
     IF( B.NE.ZERO ) THEN
        IF( SIGN( ONE, B ).EQ.SIGN( ONE, C ) ) THEN
*
*                    Real eigenvalues: reduce to upper triangular form
*
           SAB = SQRT( ABS( B ) )
           SAC = SQRT( ABS( C ) )
           P = SIGN( SAB*SAC, C )
           TAU = ONE / SQRT( ABS( B+C ) )
           A = TEMP + P
           D = TEMP - P
           .
           .
           .

Maybe an alternative approach would be to compare against machine accuracy instead of zero but that's not my field of expertise.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions