При взаимодействии фрагмента кода Fortran 2003 (или выше) с MATLAB от MEX я с удивлением обнаружил, что MEX изменяет тип логической схемы по умолчанию . Это фатально , поскольку часть идеально компилируемого кода Fortran может не быть мексифицирована из-за несоответствия типов , что и произошло в моем проекте.

Вот минимальный рабочий пример.

Назовите следующий код как «test_kind.F», скомпилируйте его с помощью mex test_kind.F в MATLAB, а затем запустите test_kind в MATLAB. Это создаст простой текстовый файл с именем fort.99, который содержит два числа «4», а затем «8» в результате выполнения инструкций WRITE.

[ Обновление : в исходном вопросе я перевернул «4» и «8», так что я подумал, что вид (ieee_is_nan (1.0)) был изменен; действительно, это вид (.false.) изменяется с 4 на 8, а вид (ieee_is_nan (1.0)) всегда остается 4 . Так что все полностью объясняется красивым ответом от @francescalus, спасибо!]

! test_kind.F
! Tested by MATLAB 9.8.0.1323502 (R2020a) with GNU Fortran (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0

#include "fintrf.h"

      subroutine mexFunction(nlhs, plhs, nrhs, prhs)

      use ieee_arithmetic, only : ieee_is_nan
      implicit none
      mwPointer plhs(*), prhs(*)
      integer nlhs, nrhs

      write(99, *) kind(ieee_is_nan(1.0))  ! This prints a number in fort.99
      write(99, *) kind(.false.)  ! A benchmark, which should print the same number in fort.99
      close(99)

      end subroutine mexFunction

Напротив, здесь тот же код без интерфейса с MATLAB, который печатает «4» и «4» на экране при компиляции с помощью gfortran. Я думал, что эти два числа всегда должны быть равны друг другу, хотя конкретное значение зависит от компилятора и не должно быть 4 - например, для компилятора nagfor они равны 3.

[ Обновление : Вышеупомянутое предположение оказывается неверным. С некоторыми параметрами компилятора kind(ieee_is_nan(1.0) и kind(.false.) могут фактически отличаться друг от друга и, следовательно, нарушать спецификации в стандарте Fortran. См. Ответ @francescalus и мое резюме в конце этого вопроса.]

! test_kind.f90
! Tested by GNU Fortran (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0

program test_kind

use ieee_arithmetic, only: ieee_is_nan
implicit none

write(*, *) kind(ieee_is_nan(1.0))  ! This prints a number on STDOUT (screen)
write(*, *) kind(.false.)   ! A benchmark, which should print the same number on STDOUT (screen)

end program test_kind

Для справки ниже приведен раздел IEEE_IS_NAN в стандарте Fortran 2018 < / а>. Он указывает, что IEEE_IS_NAN возвращает «логику по умолчанию», которая, я думаю, должна быть идентична типу внутренней константы .true. или .false. --- или я ошибся?

17.11.13 IEEE_IS_NAN (X)
1 Description. Whether a value is an IEEE NaN.
2 Class. Elemental function.
3 Argument. X shall be of type real.
4 Restriction. IEEE_IS_NAN (X) shall not be invoked if IEEE_SUPPORT_NAN (X) has the value false.
5 Result Characteristics. Default logical.
6 Result Value. The result has the value true if the value of X is an IEEE NaN; otherwise, it has the value false.

Мне кажется необычным, что MEX может изменить тип логического по умолчанию. Возможно, существует опция MEX, которая может исправить это поведение, но почему это должно быть по умолчанию вообще?

Я пробовал тот же код на других машинах с другими версиями компилятора MATLAB и Fortran. Права такие же.

  1. MATLAB 9.7.0.1319299 (R2019b) Обновление 5 с GNU Fortran (GCC) 8.3.1 20191121 (Red Hat 8.3.1-5):

    kind(ieee_is_nan(1.0))= 4, kind(.false.) = 8

    Тот же компилятор без взаимодействия с MATLAB:

    kind(ieee_is_nan(1.0)) = 4 = kind(.false.)

  2. MATLAB 9.5.0.1049112 (R2018b) Обновление 3 с GNU Fortran (Ubuntu 9.3.0-17ubuntu1 ~ 20.04) 9.3.0:

    kind(ieee_is_nan(1.0)) = 4, kind(.false.) = 8

    Тот же компилятор без взаимодействия с MATLAB:

    kind(ieee_is_nan(1.0)) = 4 = kind(.false.)

  3. Windows 10 ) MATLAB 9.5.0.944444 (R2018b) с компилятором Intel (R) Visual Fortran Intel (R) 64, версия 19.1.1.216, сборка 20200306

    kind(ieee_is_nan(1.0)) = 4, kind(.false.) = 8

    Тот же компилятор без взаимодействия с MATLAB:

    kind(ieee_is_nan(1.0)) = 4 = kind(.false.)


Резюме после принятия ответа @francescalus

  1. Оказывается, несоответствие между kind(.false.) и kind(ieee_is_nan(1.0)) происходит из-за опции gfortran -fdefault-integer-8, которая принята MEX по умолчанию. Этот параметр заставляет gfortran использовать 64-битное целое число и 64-битное логическое значение в качестве типа по умолчанию, но не меняет возвращаемый тип ieee_is_nan, даже несмотря на то, что стандарт Fortran указывает, что ieee_is_nan должен возвращать значение по умолчанию логичный вид. Это может быть связано с тем, что ieee_is_nan на самом деле не является внутренней процедурой.

  2. Обратите внимание, что ifort (версия ifort (IFORT) 2021.2.0 20210228) и nagfor (NAG Fortran Compiler Release 7.0 (Yurakucho) Build 7036) ведут себя одинаково, при этом соответствующая опция - -i8 для них обоих.

  3. Учитывая эти факты, я решаю не использовать ieee_is_nan, а реализовать свой собственный is_nan. В противном случае мой пакет не будет скомпилирован из-за несоответствия типов, если пользователи решат использовать 64-битное целое число по умолчанию (не задумываясь о логическом виде; зачем им?). А если серьезно, то MATLAB уже сделал такой выбор для всех своих пользователей, не сказав им.

Большое спасибо за любые комментарии или критику.

7
Nuno 5 Сен 2021 в 06:38

2 ответа

Лучший ответ

По умолчанию MEX компилируется с опцией gfortran -fdefault-integer-8. То, как gfortran справляется с этим, приводит к тому, что вы видите.

Рассмотрим программу, отличную от MEX

  use, intrinsic :: ieee_arithmetic, only : ieee_is_nan, ieee_support_nan
  implicit none

  if (.not.ieee_support_nan(1.)) error stop "Lack of support"
  print*, KIND(ieee_is_nan(1.)), KIND(.TRUE.)

end

Скомпилировано с / без -fdefault-integer-8.

Эта опция делает целочисленные и логические переменные по умолчанию шириной 8 байтов (и имеют параметр вида 8).

Все это имеет смысл. Однако похоже, что gfortran не изменяет параметр вида результата функции для функций во встроенном модуле ieee_arithmetic с помощью этой опции (в некотором смысле это разумно: он просто берет то, что говорят "предварительно скомпилированные" файлы модуля , что означает «возврат логичен (4)».

(Если что-то кажется сбивающим с толку, когда вы видите, что он действительно , похоже, использует правильные типы в другом внутреннем модуле, таком как iso_c_binding, обратите внимание, что этот второй внутренний модуль не поддерживается поставляемым файлом модуля . Вы увидите такое же поведение для модулей IEEE с внутренним модулем OpenMP, который также предоставляется в виде файла.)

В качестве обходного пути вы можете использовать LOGICAL(ieee_is_nan(1.)), чтобы все снова совпало.


Параметры компилятора, такие как -fdefault-integer-8, не игрушки. Их следует использовать только в самых редких случаях. С gfortran, если вы используете эту опцию отдельно, вы говорите компилятору: «Давай, делай что хочешь: меня не волнует, относишься ли ты к моей программе в соответствии со стандартом Fortran». В этом случае это выбор, который MATLAB сделал за вас.

В частности, если вы используете параметр компилятора для изменения параметров типа вещей (таких как типы по умолчанию или схему нумерации видов, как показано, например, в компиляторе NAG), вы должны скомпилировать все составные части вашей программы с этим параметром, чтобы уверен в последовательности. Как видно из этого случая, сделать эти ужасно опасные параметры по умолчанию не всегда просто. Даже внутренние модули могут быть включены в список вещей, которые необходимо учитывать для согласованности.

3
francescalus 5 Сен 2021 в 13:51

Конечно, эти два числа зависят от компилятора - для компилятора nagfor они равны 3, но я думал, что они всегда должны быть равны друг другу.

НЕТ! Не существует неявного соответствия между номерами видов одного типа и номерами видов другого типа! Пусть вас не вводит в заблуждение обычное использование размеров байтов для чисел типа - это просто соглашение, которое некоторые компиляторы приняли, чтобы быть более знакомыми программистам, привыкшим к расширению integer*4.

Для компилятора было бы совершенно правильно иметь типы 14, 27 и 830 для LOGICAL и 2,9 и 13 для REAL. Единственное, на что вы можете рассчитывать:

  • Целое число по умолчанию, действительное по умолчанию и логическое по умолчанию занимают одну «числовую единицу хранения».
  • Двойная точность и комплекс по умолчанию занимают две числовые единицы хранения

(См. Fortran 2018 19.5.3.2 Последовательность хранения)

Конечно, как уже указывалось, если вы используете параметры компилятора для изменения типа по умолчанию для одного типа, но не для другого, вы нарушаете это правило.

Для дальнейшего чтения см. Мое сообщение в блоге Доктор Фортран в фильме« Все добры »

0
Steve Lionel 5 Сен 2021 в 13:27