gpt4 book ai didi

c++ - 调用从 C++ 返回 KIND=10 复数值的 Fortran 函数

转载 作者:行者123 更新时间:2023-12-01 14:24:56 29 4
gpt4 key购买 nike

我有一堆旧的 F77 源代码(通常在 x86_64gfortran -std=legacy 上编译)。它在形式上包含了相当多的功能:

      double complex function f(x, y, i)
double precision x, y
integer i
f = cmplx(x, y) * i
return
end

我需要从一些 C++ 代码调用这些函数(通常在 x86_64g++ 上编译)。

  1. 它使用默认的 Fortran KIND=8 :

    extern "C" { std::complex<double> f_(double *x, double *y, int *i); }
  2. 当我强制执行默认的 Fortran KIND=4 时它起作用了使用 -freal-8-real-4选项:

    extern "C" { std::complex<float> f_(float *x, float *y, int *i); }
  3. 当我强制执行默认的 Fortran KIND=16 时它起作用了使用 -freal-8-real-16选项(在 C++ 中为 #include <quadmath.h> ):

    extern "C" { __complex128 f_(__float128 *x, __float128 *y, int *i); }

    令我惊讶的是,在这种情况下,它似乎也适用于(返回值在 *z 中):

    extern "C" { void f_(__complex128 *z, __float128 *x, __float128 *y, int *i); }

    以上两个原型(prototype)中哪个是(更多?)合适的?

  4. 我的问题是我无法让它与我想要的默认 Fortran 一起工作 KIND=10使用 -freal-8-real-10选项。在 Fortran 内部,kind , precision , rangesizeof直接对应于 C++ long double 的返回值.所以,我尝试了:

    extern "C" { std::complex<long double> f_(long double *x, long double *y, int *i); }
    extern "C" { void f_(std::complex<long double> *z, long double *x, long double *y, int *i); }
    extern "C" { void f_(long double *x, long double *y, int *i, std::complex<long double> *z); }

    但我根本无法让它工作。

    也许我需要为 gfortran 添加一些特殊标志和/或 g++调用以使 C++ 检索 Fortran KIND=10复杂的值(value)?注意:我认为我不能使用 -ff2c .

更新(2020.08.04):我已经能够欺骗 C++ 编译器,使其似乎为任何 Fortran 生成正确的代码 KIND=4,8,10 .诀窍是使用 ISO C99 _Complex在 C++ 中(注意:此技巧仅对 KIND=10 是必需的,但它实际上也适用于 KIND=4,8):

#include <complex.h>
#define C99KIND long double /* it can be "float", "double" or "long double" */
extern "C" { C99KIND _Complex f_(C99KIND *x, C99KIND *y, int *i); }

请注意,在 C++ 中,您不能使用例如long double complex不过还好long double _Complex还是可以的。

ISO C99 的可用性 _Complex在 C++ 中是相当有限的。例如,使用 -std=c++11 (或更新)甚至是最基本的 creal*cimag*功能消失。

因此,最好的办法是立即将返回值复制到一些标准的 C++ 模板化复杂变量中,例如使用类似的东西(注意:f_ 返回 C99KIND _Complex):

std::complex<C99KIND> z = f_(&x, &y, &i);

最佳答案

如果您想正确完成这项工作,请学习如何实际使用 Fortran 的种类类型参数并将您的 Fortran 代码正确移植到 REAL(10)。是的,我知道 10 不可移植;然而,我们正在讨论特定的 Fortran 处理器。

考虑一下,

function f(x, y, i) result(r) bind(c, name='f')
use iso_c_binding, only : ep => c_long_double
implicit none
complex(ep) r
real(ep), intent(in), value :: x, y
integer, intent(in), value :: i
r = cmplx(x, y, ep) * i
end function f

而且,由于我不使用 C++,但您应该能够根据需要更新 C

#include <complex.h>
#include <stdio.h>
#include <stdlib.h>

long double complex f(long double, long double, int);

int
main(void)
{
int i;
long double x, y;
long double complex z;

i = 42;
x = 1;
y = 2;
printf("%.10Lf %.10Lf\n", x, y);

z = f(x, y, i);
x = creall(z);
y = cimagl(z);
printf("%.10Lf %.10Lf\n", x, y);
return 0;
}

% gfortran -c a.f90
% gcc -o z b.c a.o -lm
% ./z
1.0000000000 2.0000000000
42.0000000000 84.0000000000

OP 声明他不会为他/她的 Fortran 代码的正确移植而烦恼,因此,必须使用编译器选项来神奇地(是的,这很神奇)进行移植。这是一个例子

% cat a.f90
double complex function f(x, y, i)
implicit none
double precision :: x, y
integer i
f = cmplx(x, y) * i ! cmplx my not do what you expect
end function f

% cat b.c
#include <complex.h>
#include <stdio.h>
#include <stdlib.h>

long double complex f_(long double *, long double *, int *);

int
main(void)
{
int i;
long double x, y;
long double complex z;

i = 42;
x = 1;
y = 2;
printf("%.10Lf %.10Lf\n", x, y);

z = f_(&x, &y, &i);
x = creall(z);
y = cimagl(z);
printf("%.10Lf %.10Lf\n", x, y);
return 0;
}

% gfcx -c -freal-8-real-10 a.f90
% gcc -o z b.c a.o -lm
% ./z
1.0000000000 2.0000000000
42.0000000000 84.0000000000

不妨选择三重奏。下面是第二个示例中与上述文件 a.f90 一起使用的 C++ 代码。

#include <iostream>
#include <complex>
#include <cmath>

extern "C" { std::complex<long double> f_(long double *, long double *, int *); }

int main()
{
std::complex<long double> z;
long double x, y;
int i;

i = 42;
x = 1;
y = 2;
z = f_(&x, &y, &i);

std::cout << z << '\n';
}

关于c++ - 调用从 C++ 返回 KIND=10 复数值的 Fortran 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63230415/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com