Передача массива C ++ в подпрограмму Fortran вызывает появление значений nan в результате

Я пытаюсь использовать код Fortran из приложения C ++. В частности, я пытаюсь взаимодействовать с SLATEC drc3jj.f. Однако подпрограмма Fortran возвращает массив, размер которого зависит от параметров, передаваемых в функцию.

Если размер массива равен 1, то выводимый мной массив C ++ содержит соответствующее значение. Однако, если этот размер больше единицы, массив C ++ содержит NaN, где должны быть выходные значения.

Ниже приведен код, который я использую. Это просто связывает подпрограмму Fortran с приложением C ++.

#ifndef FORTRANLINKAGE_H
#define FORTRANLINKAGE_H

extern "C"{
extern void drc3jj_(double*,double*,double*,double*,double*,
double*,double [],int*,int*);
}

#endif // FORTRANLINKAGE_H

Мясо здесь внизу. Мы фактически вызываем подпрограмму Fortran из C ++ и выводим вывод:

#include "fortranLinkage.h"
#include <iostream>
#include <stdlib.h>

using namespace std;

void wigner3j(double l2, double l3, double m2, double m3, double coeff [])
{
double l1min,l1max;
int ierr,size(3);

drc3jj_(&l2,&l3,&m2,&m3,&l1min,&l1max,coeff,&size,&ierr);

cout << "Min: " << l1min << "\t Max: " << l1max << "\t Err: " << ierr << endl;
}

int main(int argc, char const *argv[])
{
int l1(atoi(argv[1])),l2(atoi(argv[2])),m2(atoi(argv[3])),m3(atoi(argv[4]));
double coeff [3];

wigner3j(l1,l2,m2,m3,coeff);

for (int i=0;i<3;i++)
{
cout << coeff[i] << endl;
}
return 0;
}

Если мы вызываем программу с ./myProgram 2 8 2 8, он правильно выводит 1 / sqrt (21). Однако, если мы попробуем ./myProgram 2 8 2 7где размер массива фактически равен 2, мы получаем такой результат:

Min: 9   Max: 10     Err: 0
-nan
-nan
2.08175e-317

На самом деле NaN имеют правильный знак.

В любом случае, есть ли другой (правильный) способ передачи массивов C ++ в Fortran? Это вообще проблема?

2

Решение

Проблема заключается не в интерфейсе между C ++ и Fortran, а скорее в устаревшей реализации Fortran. Файл drc3jj.f является частью библиотеки SLATEC, в которой есть служебные функции, которые возвращают константы, которые зависят от машины, на которой он работает (машинные константы). Они определены в файлах d1mach.f, i1mach.f а также r1mach.f,

Тем не менее, начиная с Фортрана 95, существуют такие особенности, как huge(), tiny(), spacing() а также epsilon() которые гарантированно вернут правильные значения для любой машины.

Решение, таким образом, состоит в том, чтобы удалить любую ссылку на d1mach(int) в drc3jj() подпрограммы и заменить их на соответствующие встроенные функции.

Более того, прямая ссылка на подпрограммы на Фортране всегда может быть хитрой, поскольку она зависит от компилятора; лучше использовать iso_c_binding в fortran90 определить интерфейс к C для вас безопасным способом:

!wrapper.f90:

subroutine drc3jj_wrap(l2, l3, m2, m3, l1min, l1max, thrcof, ndim, ier) bind(C)

use iso_c_binding
implicit none

real(c_double), value, intent(in)           :: l2, l3, m2, m3
real(c_double), intent(out)                 :: l1min, l1max
real(c_double), dimension(ndim), intent(out):: thrcof
integer (c_int), value, intent(in)          :: ndim
integer (c_int), intent(out)                :: ier

interface
SUBROUTINE DRC3JJ (L2, L3, M2, M3, L1MIN, L1MAX, THRCOF, NDIM, IER)
INTEGER NDIM, IER
DOUBLE PRECISION L2, L3, M2, M3, L1MIN, L1MAX, THRCOF(NDIM)
end SUBROUTINE DRC3JJ
end interface

call DRC3JJ(l2, l3, m2, m3, l1min, l1max, thrcof, ndim, ier)

end subroutine drc3jj_wrap

а также

// fortranLinkage2.h
#ifndef FORTRANLINKAGE_H
#define FORTRANLINKAGE_H

extern "C"{
extern void drc3jj_wrap(double l2, double l3, double m2, double m3,
double *l1max, double *l2max, double *thrcof,
int ndim, int *ier);
}

#endif // FORTRANLINKAGE_H

а потом просто позвони

void wigner3j(double l2, double l3, double m2, double m3, double coeff [])
{
double l1min,l1max;
int ierr,size(3);

drc3jj_wrap(l2,l3,m2,m3,&l1min,&l1max,coeff,size,&ierr);

cout << "Min: " << l1min << "\t Max: " << l1max << "\t Err: " << ierr << endl;
}

и компиляция и запуск дает

$ g++ -c foo2.cc
$ gfortran -c wrapper.f90
$ gfortran -c drc3jj.f
$ g++ -o foo2 foo2.o wrapper.o drc3jj.o -lgfortran
$ ./foo2 2 8 2 8
Min: 10  Max: 10     Err: 0
0.218218
2.07738e-317
0
gpc-f103n084-$ ./foo2 2 8 2 7
Min: 9   Max: 10     Err: 0
-0.102598
-0.19518
0
8

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]