-
Notifications
You must be signed in to change notification settings - Fork 480
Description
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*SNThis 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.