在Windows上调用来自Julia(0.6.2)的Fortran函数的错误结果

时间:2018-01-30 03:56:38

标签: fortran julia

我从较大的遗留代码库中找到了一个小辅助子程序,它计算了一个向量的一些统计数据并单独编译它并试图从Julia(0.6.2)调用它来进行练习。

首先,我找到了一些论坛帖子和话语线程,但我想仔细检查,因为语言很快就开发了,有些可能会使用过时的语法like this one。另外,我使用Windows和许多论坛帖子,我遇到了参考.so文件,我的理解是针对UNIX系统,而不是Windows?

我正在引用的Fortran代码(称为meansd.f)是:

SUBROUTINE MEANSD (A,N,ABAR,VAR,STD)
IMPLICIT NONE
C     COMPUTE THE MEAN AND STANDARD DEVIATION OF A REAL VECTOR.
C     PARAMETERS:
C     A: REAL VECTOR TO COMPUTE MEAN AND STANDARD DEVIATION OVER.
C     N: LENGTH OF A.
C     ABAR: COMPUTED MEAN OF A.
C     VAR: VARIANCE OF A.
C     STD: COMPUTED STANDARD DEVIATION OF A.
C
REAL A(N)
INTEGER I,N
DOUBLE PRECISION APX, ABAR, VAR, STD, SUM, SUMSQ, DN, Z
C
DN=N
APX=0D0
DO 30 I=1,N
APX=APX+A(I)
30 CONTINUE
APX=APX/DN
SUM=0D0
SUMSQ=0D0
DO 40 I=1,N
Z=A(I)-APX
SUM=SUM+Z
SUMSQ=SUMSQ+Z*Z
40 CONTINUE
ABAR=SUM/DN
VAR=(SUMSQ-(SUM*ABAR))/(DN-1)
STD=DSQRT(VAR)
ABAR=ABAR+APX
RETURN
END

我使用gcc编译了这个

gcc (x86_64-posix-seh-rev1, Built by MinGW-W64 project) 7.2.0

主要跟随this question,完成时没有错误:

gfortran -c meansd.f
gfortran -shared -fPIC  meansd.o -o meansd.dll 

当我用Libdl进行测试时,一切似乎都恢复正常,我得到一个指针

path=joinpath(pwd(),"meansd.dll")
dl=Libdl.dlopen(path)

然后我尝试将ccall函数构建为:

a_ref=Ref{Array{Float64,1}}([5.0,5.0,5.0,5.0,5.0])
i_ref=Ref{Int64}(1)
n_ref=Ref{Int64}(length([5.0,5.0,5.0,5.0,5.0]))
apx_ref=Ref{Float64}(0)
abar_ref=Ref{Float64}(0)
var_ref=Ref{Float64}(0)
std_ref=Ref{Float64}(0)
sum_ref=Ref{Float64}(0)
sumsq_ref=Ref{Float64}(0)
dn_ref=Ref{Float64}(0)
z_ref=Ref{Float64}(0)

ccall((:meansd_,"meansd.dll"),Void,(
Ref{Array{Float64,1}},#A
Ref{Int64}, #I
Ref{Int64}, #N
Ref{Float64}, #APX #I think I have to pass these too?
Ref{Float64}, #ABAR
Ref{Float64}, #VAR
Ref{Float64},  #STD
Ref{Float64}, #SUM
Ref{Float64}, #SUMSQ
Ref{Float64}, #DN
Ref{Float64}, #Z
),a_ref,i_ref,n_ref,apx_ref,
abar_ref,var_ref,std_ref,sum_ref,sumsq_ref,dn_ref,z_ref)

这完成了我已经完成的内容,但abar_ref作为NaN返回。然后我创建了一个Float / Int 32版本,完成但abar_ref作为0.0返回。我以为我应该使用64位,因为我用64位gfortran编译。

我认为我搞砸了如何传递数组,但是我已经尝试了许多不同的接近我想到的但没有成功。

我也很困惑何时使用Julia类型与ccall类型(Float32 vs Cint?)。我已经看到了两个分散在网络中的例子,但是如果我正确阅读,那么在这一点后面有内置的转换语句可以处理它吗?

1 个答案:

答案 0 :(得分:1)

编辑扩展说明

根据皮埃尔的评论,我回过头来重新阅读文档,并设法获得了一个有效的版本:

我首先删除了实例化除函数调用中命名的变量之外的任何变量:

SUBROUTINE MEANSD (A,N,ABAR,VAR,STD)

接下来,我重新阅读the documentation section on calling FORTRAN,特别是此警告部分。

  

警告

     

对于字符串参数(char *),Julia类型应该是Cstring(如果是NUL-   预期终止数据)或Ptr {Cchar}或Ptr {UInt8}   否则(这两种指针类型具有相同的效果),如上所述   上面,不是String。同样,对于数组参数(T []或T *),   Julia类型应该再次是Ptr {T},而不是Vector {T}。

结果代码是:

#declare variables

a=Float32[1.0,2.0,3.0,4.0,5.0]  #changed values from OP to have non-zero var/sd for demo
n=length(a) #works as Int32(length(a)) as well?
abar_ref=Ref{Float64}(0)
var_ref=Ref{Float64}(0)
std_ref=Ref{Float64}(0)

#call function

ccall((:meansd_,"meansd.dll"),Void,(
Ptr{Float32},#A
Ref{Int32}, #N
Ref{Float64}, #ABAR
Ref{Float64}, #VAR
Ref{Float64},  #STD
),a,n,abar_ref,var_ref,std_ref)

#check results
abar_ref[] #returns 3.00

var_ref[] #returns 2.50

std_ref[] #returns 1.58