當用MEX將一段Fortran 2003(或以上)代碼與MATLAB對接時,我驚訝地發現,MEX改變了默認邏輯的型別。這是致命的,因為一段完全可以編譯的Fortran代碼可能由于型別不匹配而無法被解釋,這在我的專案中確實發生過。
下面是一個最小的作業例子。
將以下代碼命名為 "test_kind.F",在MATLAB中通過mex test_kind.F編譯它,然后在MATLAB中運行test_kind。這將產生一個名為fortune.99的純文本檔案,其中包含兩個數字 "4",然后是 "8 "作為WRITE指令的結果。
! test_kind.F
! 通過MATLAB 9.8.0.1323502(R2020a)與GNU Fortran(Ubuntu 9。 3.0-17ubuntu1~20.04) 9.3.0。
#include "fintrf.h".
子程式mexFunction(nlhs, plhs, nrhs, prhs)
使用ieee_arithmetic,只有:ieee_is_nan
隱式無
mwPointer plhs(*), prhs(*)
整數 nlhs, nrhs
write(99, *) kind(ieee_is_nan(1.0) ! 這將列印出一個在堡壘.99的數字。
write(99, *) kind(.false。) ! 一個基準,應該在fort.99中列印相同的數字。
close(99)
end subroutine mexFunction
我以為這兩個列印出來的數字應該總是相互相等的,盡管具體數值取決于編譯器,而且不需要是4或8。(正如Fortran博士@SteveLionel所強調的,Fortran標準在這些種類的數字和用來表示資料的位元組數之間沒有關系。參見Steve的博客Doctor Fortran in "It Takes All KINDs",對這個話題進行了很好的闡述。)
[Update。上述關于kind(ieee_is_nan(1.0)=kind(.false.)的推測結果是錯誤的,盡管它是Fortran 2018標準建議的。 通過某些編譯器選項,kind(ieee_is_nan(1.0)和kind(.false.)實際上可以彼此不同,從而違反了Fortran標準中的規范。請看@francescalus的回答和我在本問題末尾的總結。]
[更新:在最初的問題中,我翻轉了 "4 "和 "8",所以我認為kind(ieee_is_nan(1.0))/code>被改變了;事實上,是kind(.false.)/code>從4被改變為8,而。所以一切都被@francescalus的漂亮回答完全解釋了,謝謝你!]kind(ieee_is_nan(1.0))/code>始終保持4
為了比較,這里是一個關于 "我的 "的例子。
作為比較,這里是沒有與MATLAB對接的相同代碼,用gfortran編譯時,在螢屏上列印出 "4 "和 "4"。 使用nagfor編譯器時,數字仍然相等,雖然變成了3.
。! test_kind.f90
! 經GNU Fortran測驗(Ubuntu 9.3.0-17ubuntu1~ 20.04) 9.3.0.
程式test_kind
使用ieee_arithmetic,只有:ieee_is_nan
隱式無
write(*, *) kind(ieee_is_nan(1.0) ! 這將在STDOUT(螢屏)上列印一個數字
write(*, *) kind(.false。) ! 一個基準,它應該在STDOUT(螢屏)上列印相同的數字
end program test_kind
接受了@francescalus的答案后的總結
事實證明,
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并不是一個真正的內在存盤程序,而只是來自內在模塊ieee_arithmetic的一個存盤程序。注意ifort(版本ifort(IFORT)2021.2.0 20210228)和nagfor(NAG Fortran Compiler Release 7.0 (Yurakucho) Build 7036)的行為也與上述方式相同,它們的對應選項是
-i8。因此,編譯器供應商同意,當某些選項被強制執行時,打破一些內在模塊的一致性是可以的。這讓我很驚訝。幸運的是,flang(在clang 7.1.0下)遵循Fortran標準,即使強加了-fdefault-integer-8,保持kind(is_ieee_nan(1.0)) == kind(.false.)--所以這并不是一個不可能完成的任務。當
-i8被采用時,NAG編譯器nagfor對ieee_is_nan發出警告,指出它們是不兼容的;然而,gfortran和ifort,即使你分別用-Wall -Wexta和-warn all來呼叫它們,也保持絕對沉默。這更讓我感到驚訝。鑒于這些事實,我決定不使用
ieee_is_nan,而是實作我自己的is_nan,并且對內在模塊提供的所有程式保持警惕(或遠離)。否則,如果用戶選擇強制使用64位整數作為默認型別(而不考慮邏輯型別;為什么要這樣做?更嚴重的是,MATLAB已經在不告訴用戶的情況下為其所有用戶做出了這樣的選擇。我很高興看到我的問題導致了有趣的討論。由于一些編譯器似乎需要就這里發現的問題進行改進(你們可能有不同的意見),我在Fortran Discourse上發了一篇關于這個話題的帖子。我希望它能引起社區的更多關注,至少有人會給gfortran這樣的開源編譯器打補丁。
非常感謝您的任何評論或批評。
uj5u.com熱心網友回復:
默認情況下,MEX用gfortran選項-fdefault-integer-8進行編譯。gfortran處理這個問題的方式導致了你看到的結果。
考慮一下非MEX程式
use, intrinsic :: ieee_arithmetic, only : ieee_ is_nan, ieee_support_nan
隱式無
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中的函式結果種類引數(這在某種程度上是合理的:它只是采用了 "預編譯 "模塊檔案的說法,即 "return is logical(4)"。
(如果當你看到它確實似乎在另一個內在模塊(如iso_c_binding)中使用了正確的種類時,事情顯得很混亂,請注意這第二個內在模塊并沒有被提供的模塊檔案支持。你將看到IEEE模塊與OpenMP內在模塊的相同行為,OpenMP內在模塊也是以檔案形式提供的。
作為一種變通方法,你可以使用LOGICAL(ieee_is_nan(1.))來使事情再次匹配。
像
-fdefault-integer-8這樣的編譯器選項不是玩具。它們應該只在最罕見的情況下使用。在 gfortran 中,如果你單獨使用這個選項,你就是在告訴編譯器:"去吧,做你想做的事。我不在乎你是否按照Fortran標準的規定來處理我的程式"。在這種情況下,這是MATLAB為您做出的選擇。
特別是,如果您使用編譯器選項來改變事物的種類引數(例如默認種類,或例如在NAG編譯器中看到的種類編號方案),您必須用該選項編譯您程式的所有組成部分,以確保一致性。正如在這個案例中所看到的,這并不總是容易的,這使得這些選項作為默認值是非常危險的。甚至內在的模塊也可能被列入需要考慮一致性的串列中。
uj5u.com熱心網友回復:
(編輯--我誤讀了這句話。見下面的評論。)
當然,這兩個數字取決于編譯器------對于nagfor編譯器來說,它們是3,但我認為它們應該總是相互相等的。
不! 一個型別的種類編號和另一個型別的種類編號之間沒有隱含的對應關系! 不要被常見的使用位元組大小的種類號所迷惑--這只是一些編譯器采用的慣例,以便讓習慣于整數*4擴展的程式員看起來更熟悉。
對于一個編譯器來說,LOGICAL有14、27和830種,REAL有2、9和13種,這都是完全有效的。你唯一可以指望的是:
- 默認的整數、默認的實數和默認的邏輯數各占一個 "數字存盤單元" 雙精度和默認復數占用兩個數字存盤單元。
(見Fortran 2018 19.5.3.2 存盤序列)
當然,正如已經指出的那樣,如果你使用編譯器選項來改變一種型別的默認種類,而不是另一種型別,你就會破壞這一規則。
如需進一步閱讀,請參閱我的博文,Doctor Fortran in "It Takes All KINDs"
。轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/309715.html
標籤:
