Как проверить, что fortran real совместим с C float?

Я соединяю некоторый устаревший код между fortran и C, используя привязку iso-c и модули/интерфейсы и т. д.

Обычно при передаче массива fortran в C я просто копировал его в другой массив правильного типа iso-c-binding. Однако некоторая часть кода требует, чтобы я передал большой (несколько ГБ) массив. В этом случае копирование не имеет смысла. Кроме того, поскольку он исходит из стороннего кода, изменение типа массива fortran no use real(c_float) также невозможно.

Однако я был бы рад создать исключение или не скомпилировать, если базовый массив fortran был несовместим с API-интерфейсом C на основе float.

Есть ли способ проверить, совместим ли real по умолчанию с C float или double во время компиляции (или во время выполнения)?

Я использую компиляторы Intel fortran и C, если это имеет значение.


person Michael Anderson    schedule 23.03.2016    source источник
comment
Почему вы говорите, что копирование многогигабайтного массива бессмысленно? Какой тип оборудования является целевым?   -  person wallyk    schedule 23.03.2016
comment
@wallyk В первую очередь потому, что я уже почти исчерпал лимит памяти на машине. Добавление еще 4 ГБ в используемую память приведет к падению приложения или подтолкнет меня к свопингу, что неприемлемо.   -  person Michael Anderson    schedule 23.03.2016
comment
Я предлагаю посмотреть руководство по компилятору Fortran. Однако в прежние времена (также известный как Fortran 77) REAL также был REAL*4, а не REAL*8 для двойной точности — была ли это ДВОЙНАЯ ТОЧНОСТЬ? — так что шансы, что Fortran REAL — это то же самое, что C float, очень высоки. И если бы по какой-то случайности или ухищрению оказалось, что Fortran REAL был C double, вы бы смогли использовать нескопированный массив, не так ли? AFAICR, если вы измените массив в коде C, он изменится и в вызывающей функции — точно так же, как в C.   -  person Jonathan Leffler    schedule 23.03.2016
comment
Вы можете попробовать написать real (OriginalArray, c_float). Если OriginalArray имеет тот же тип, что и c_float, можно надеяться, что встроенный real записывается сразу после массива. Если это другой тип, то будет создана временная копия типа c_float. Может памяти хватило бы...   -  person M. S. B.    schedule 23.03.2016
comment
@JonathanLeffler в былые времена шансы работать со странной машиной, где эти простые предположения не соответствовали действительности, были намного выше, чем сегодня (различные 24-битные или 48-битные машины и другие).   -  person Vladimir F    schedule 23.03.2016


Ответы (2)


Сравнение типа REAL по умолчанию и типа REAL, совместимого с float в C, представляет собой простое целочисленное сравнение.

Вы можете заставить это сравнение вызвать ошибку компиляции или ошибку времени выполнения.

USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_FLOAT    

! -99 is never a valid kind - so this will fail at compile time 
! if the test condition is false.
REAL(MERGE(KIND(1.0), -99, C_FLOAT == KIND(1.0)) :: dummy

or

IF (C_FLOAT /= KIND(1.0))  &
    ERROR STOP 'Default REAL isn''t interoperable with FLOAT!!'

Сам C_FLOAT может иметь отрицательное значение, если процессор Fortran не имеет типа REAL, который с ним взаимодействует.

(Для некоторых версий 15.0.x ifort можно использовать следующее для варианта проверки времени компиляции:

USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_FLOAT
INTEGER, PARAMETER :: dummy_rk = MERGE(C_FLOAT, -99, KIND(1.0) == C_FLOAT)
! -99 is never a valid kind - so this will fail at compile time 
! if the test condition is false.
REAL(dummy_rk) :: dummy
person IanH    schedule 23.03.2016
comment
Для проверки времени компиляции я получаю error #6683: A kind type parameter must be a compile-time constant. [MERGE] - person Michael Anderson; 23.03.2016
comment
@MichaelAnderson Выражение, которое обеспечивает тип для переменной dummy, требует поддержки F2003. Я подозреваю, что вы не используете последнюю версию ifort. У меня работает с 16.0.2. - person IanH; 23.03.2016
comment
Да пока остановился на 15. Так что думаю, я пока застрял с версией среды выполнения, - person Michael Anderson; 23.03.2016
comment
ок - с некоторыми версиями 15.0 если упростить выражение для компилятора с некоторыми промежуточными именованными константами, то будет работать. См. редактирование. - person IanH; 23.03.2016
comment
Я не вижу, чтобы типы real*4, real по умолчанию, C_FLOAT были одинаковыми, даже если представление с плавающей запятой одинаково. Я не понимаю, что именно здесь требуется. Если вы параноик, вы можете запустить старомодные тесты, чтобы увидеть, можно ли сопоставить значения EPSILON, связанные с типами данных. - person tim18; 13.04.2018

Есть несколько проблем.

  • float C передается как double для параметров функции C и оценки выражения. Это легко преодолеть, передав указатель на структуру или массив, содержащий переменные, определенные как float (которые действительно используют 32-битное число с плавающей запятой).

  • Intel Fortran имеет несколько разновидностей Real: real*4, real*8 и real*16. По умолчанию (настройки компилятора могут изменить это), real соответствует реальному*4, а double precision соответствует real*8. См. документацию.

  • Тип long double C не имеет эквивалента в Intel Fortran, где, если бы он был реализован, он был бы real*10, который он действительно использует для промежуточных значений выражений на x86.

  • Конечно, существует проблема компоновки основного столбца и второстепенного массива столбцов (для более чем одномерных массивов) C против Fortran.

Если бы это был я, я бы проложил путь для разработки, создав тест, который передает известные данные (1.0, 2.0, 3.0, 4.0, ...) из C в Fortran и наоборот и либо печатает значения, либо сравнивает их с тем, что они должны быть. Если используются нецелые числа, обязательно используйте соответствующее сравнение эпсилон:

           if (abs (c_val - fortran_val) > 0.00001)  error();
person wallyk    schedule 23.03.2016