【问题标题】:Wrong results from Fortran function called from Julia (0.6.2) on Windows在 Windows 上从 Julia (0.6.2) 调用的 Fortran 函数的错误结果
【发布时间】:2018-01-30 03:56:38
【问题描述】:

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

首先,我找到了一些论坛帖子和讨论帖,但我想仔细检查一下,因为该语言发展迅速,有些可能使用过时的语法like this one。此外,我使用的是 Windows,而且我遇到的许多论坛帖子都参考了 .so 文件,据我了解,这些文件适用于 UNIX 系统,而不是 Windows?

我引用的整个 Fortran 代码(称为 meand.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 类型(Float32Cint?),我也感到困惑。我已经看到了这两个分散在网络上的例子,但如果我没看错的话,现在幕后有底层的转换语句可以处理这个问题吗?

【问题讨论】:

  • 嗨,第一个要检查的问题:在 fortran 中,默认的“REAL”和“INTEGER”类型取决于编译器。在典型的 64 位系统上,整数和实数的常见类型是 32 位(但同样,这是“处理器相关的”)。在您的情况下,可能的类型是 32 位实数、32 位整数,然后是三个 64 位实数。由于调用依赖于 c 接口,您可能需要查找 Fortran 的“iso c 绑定”功能。
  • 谢谢,知道这很有趣。关于你提出的观点,我有几个问题。 1. 调用外部库时需要声明中间变量(例如APX)还是只声明列出的输入/输出? 2. C ISO 标准在 F77 代码方面的工作情况如何? some quick searching makes it seem like this is represented more in the later formats
  • 我不会太担心 F77 代码的限制:您的示例代码并不完全是 F77。 gcc 7.2 对 C 互操作性有很好的支持。 (因为它是固定形式的,也许你可以考虑缩进它?)
  • 这可能是一个有趣的错误,但我承认我不知道代码应该缩进。我从source repo 复制并粘贴了代码,整个项目中的许多文件看起来都是这样,所以我认为这就是 fortran 文件的外观。我是编译语言的新手,我不太了解完整的编译器与语言本身的能力。

标签: fortran julia


【解决方案1】:

编辑:扩展注释

按照 Pierre 的 cmets,我返回并重新阅读了文档并设法获得了一个工作版本:

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

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

接下来,我重新阅读了the documentation section on calling FORTRAN,尤其是这个警告部分。

警告

对于字符串参数 (char*),Julia 类型应该是 Cstring(如果 NUL- 预期终止数据)或 Ptr{Cchar} 或 Ptr{UInt8} 否则(这两种指针类型具有相同的效果),如所述 上面,不是字符串。同样,对于数组参数(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

【讨论】:

  • 在上面的答案中添加了 cmets,指的是文档中对我有帮助的相关点。我还在更大的项目中找到了另一个smaller helper function,我是否尝试按照我在这里学到的东西来实现它,但还没有成功地做到这一点,所以我对此的把握充其量可能是微不足道的。
猜你喜欢
  • 2015-04-07
  • 1970-01-01
  • 1970-01-01
  • 2017-03-21
  • 2015-01-13
  • 2015-09-11
  • 2018-08-19
  • 2022-07-29
  • 2013-05-17
相关资源
最近更新 更多