简体   繁体   中英

Using Lapack with 128 bit precision

I am trying to use Lapack for a 128 bit precision calculation of a matrix singular value decomposition (SVD) and I found out that there is some black compiler magic to accomplish this. The Intel Fortran compiler (ifort) supports the option -r16 which instructs the compiler to take all variables declared as DOUBLE PRECISION to be 128 bit reals. So I compiled Lapack and BLAS using:

ifort -O3 -r16 -c isamax.f -o isamax.o
ifort -O3 -r16 -c sasum.f -o sasum.o
...

To incorporate this in my program (which is C++) I can use the Intel C++ compiler (icc) with the option -Qoption,cpp,--extended_float_type which creates a data type _Quad that is a 128 bit floating point variable. My SVD example looks like this:

#include "stdio.h"
#include "iostream"
#include "vector"

using namespace std;
typedef _Quad scalar;

//FORTRAN BINDING
extern "C" void dgesvd_(char *JOBU, char *JOBVT, int *M, int *N,
    scalar *A, int *LDA,
    scalar *S,
    scalar *U, int *LDU,
    scalar *VT, int *LDVT,
    scalar *WORK, int *LWORK, int *INFO);

int main() {
    cout << "Size of scalar: " << sizeof(scalar) << endl;
    int N=2;
    vector< scalar > A(N*N);
    vector< scalar > S(N);
    vector< scalar > U(N*N);
    vector< scalar > VT(N*N);

    // dummy input matrix
    A[0] = 1.q;
    A[1] = 2.q;
    A[2] = 2.q;
    A[3] = 3.q;
    cout << "Input matrix: " << endl;
    for(int i = 0; i < N; i++) {
        for(int j = 0;j < N; j++) 
            cout << double(A[i*N+j]) << "\t";
        cout << endl;
    }
    cout << endl;

    char JOBU='A';
    char JOBVT='A';
    int LWORK=-1;
    scalar test;
    int INFO;

    // allocate memory
    dgesvd_(&JOBU, &JOBVT, &N, &N,
        &A[0], &N,
        &S[0],
        &U[0], &N,
        &VT[0], &N,
        &test, &LWORK, &INFO);
    LWORK=test;
    int size=int(test);
    cout<<"Needed workspace size: "<<int(test)<<endl<<endl;
    vector< scalar > WORK(size);

    // run...
    dgesvd_(&JOBU, &JOBVT, &N, &N,
        &A[0], &N,
        &S[0],
        &U[0], &N,
        &VT[0], &N,
        &WORK[0], &LWORK, &INFO);
    // output as doubles
    cout << "Singular values: " << endl;
    for(int i = 0;i < N; i++)
        cout << double(S[i]) << endl;
    cout << endl;
    cout << "U: " << endl;
    for(int i = 0;i < N; i++) {
    for(int j = 0;j < N; j++)
        cout << double(U[N*i+j]) << "\t";
    cout << endl;
    }
    cout << "VT: " << endl;
    for(int i = 0;i < N; i++) {
    for(int j = 0;j < N; j++)
        cout << double(VT[N*i+j]) << "\t";
    cout << endl;
    }
    return 0;
}

compiled with

icc test.cpp -g -Qoption,cpp,--extended_float_type -lifcore ../lapack-3.4.0/liblapack.a ../BLAS/blas_LINUX.a

Everything works fine this far. But the output is:

Size of scalar: 16
Input matrix: 
1       2
2       3

Needed workspace size: 134

Singular values: 
inf
inf

U: 
-0.525731       -0.850651
-0.850651       0.525731
VT: 
-0.525731       0.850651
-0.850651       -0.525731

I checked that U and VT are correct, but the singular values are obviously not. Has anyone got an idea why this happens or how one could circumvent it?
Thanks for your help.

When using external libraries with extended precision, also check whether they use old-style d1mach.f, r1mach.f, i1mach.f to get information on machine arithmetic. There may be some values to tweak here.

It can't be a problem with Lapack, which uses dlamch.f (here http://www.netlib.org/lapack/util/dlamch.f ), which uses Fortran 90 intrinsics to get these machine constants.

But it may become problematic for example with BLAS or SLATEC, if you use them.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM