how to call fortran routines from C++?

后端 未结 1 1422
没有蜡笔的小新
没有蜡笔的小新 2021-01-24 09:40

I wish to call fortran routine cbesj.f from my C++ code and how do I achieve this?

Here are steps I have done:

  1. Download cbesj.f plus dependencies from n

1条回答
  •  无人及你
    2021-01-24 10:19

    I downloaded cbesj (or zbesj) and related files from netlib, but for some reason they did not work with gfortran4.8 (on Linux x86_64). More precisely, cbesj always gives ierr=4, while I could not compile zbesj because zabs has too many arguments (but please see the update below).


    So I gave up using the original AMOS codes and tried openspecfun that is also based on AMOS. I compiled the latter simply by typing make (with gfortran), which generated libopenspecfun.a etc. Then I made the following code to test zbesj:

    #include 
    
    extern "C" {
        void zbesj_( double *zr, double *zi, double *fnu, int *kode, int *n,
                     double *Jr, double *Ji, int *nz, int *ierr );
    }
    
    int main()
    {
        int    n, nz, ierr, kode;
        double fnu, zr, zi, Jr, Ji;
    
        fnu = 2.5;
        zr = 1.0; zi = 2.0;
        n = 1; kode = 1;
    
        zbesj_( &zr, &zi, &fnu, &kode, &n, &Jr, &Ji, &nz, &ierr );
    
        printf( "fnu = %20.15f\n", fnu );
        printf( "z   = %20.15f %20.15f\n", zr, zi );
        printf( "J   = %20.15f %20.15f\n", Jr, Ji );
        printf( "nz, ierr = %d %d\n", nz, ierr );
    
        return 0;
    }
    

    and compiled as

    g++ test_zbesj.cpp libopenspecfun.a -lgfortran
    

    which gives

    fnu =    2.500000000000000
    z   =    1.000000000000000    2.000000000000000
    J   =   -0.394517225893988    0.297628229902939
    nz, ierr = 0 0
    

    Because this site says the answer is -0.394517...+ 0.297628...i, the result of zbesj seems correct.


    [Update]

    By reading the original code more carefully and also comparing with openspecfun, it was found that the user needs to uncomment appropriate sections of i1mach.f and r1mach.f (or d1mach.f) depending on the machine environment. For Linux x86_64, it seems sufficient to uncomment the section tagged with

    C     MACHINE CONSTANTS FOR THE IBM PC
    

    With this modification, the cbesj worked correctly with ierr=0; otherwise it gives ierr=4 because some constants default to 0. For the double-precision version, we also need to deal with the user-defined ZABS, whose definition conflicts with the intrinsic one. The Intel Fortran (ifort) recognizes the user-defined ZABS as such and compiles them successfully (although with a lot of warnings), while gfortran stops compiling with syntax error (assuming it to be the intrinsic one). openspecfunc avoids this problem by rewriting all the ZABS with the intrinsic one. Anyway, with the above modifications both cbesj and zbesj worked correctly (as expected).


    [Update 2]

    It turned out that using the BLAS version of d1mach.f, r1mach.f, and i1mach.f, things become even simpler because they determine machine-dependent constants automatically and we don't need to modify the codes manually. Please see the Netlib/FAQ page for details. The same trick applies to SLATEC also (e.g., this page).

    wget http://www.netlib.org/blas/i1mach.f 
    wget http://www.netlib.org/blas/r1mach.f
    wget http://www.netlib.org/blas/d1mach.f 
    

    0 讨论(0)
提交回复
热议问题