Programování ve Fortranu

Pole

Pole (arrays) jsou strukturované proměnné složené z prvků indexovaných celočíselnými indexy. Jsou popsány datovým typem (stejným pro všechny prvky), pevným počtem dimenzí (rank), velikostí (size neboli počtem prvků v každé dimenzi), tvarem (shape, shrnujícím rank + size) a dolní a horní mezí pro indexy (lbound a ubound v každé dimenzi). Popisuje se buď každé pole zvlášť nebo pomocí atributu dimension všechna pole v deklaraci. Polím téhož tvaru se říká podobná (conformable) pole. Meze a tudíž velikost polí mohou být deklarovány staticky (popisem) [1] i dynamicky (příkazem); dynamická pole jsou buď alokovatelná (atribut allocatable) nebo ukazatelová (atribut pointer). Pole mohou být konstantní (atribut parameter). Součástí deklarace pole může být inicializace skalárem nebo konstruktorem pole [2]. Dolní mez je implicitně 1, explicitně cokoliv. Jednorozměrným (1D) polím se běžně říká vektory, dvourozměrným (2D) polím matice; veličiny, které nejsou polem, se pak pro odlišení nazývají skaláry. Matice jsou v (lineárně řazené) paměti ukládány po sloupcích, podobně prvky vícerozměrných polí jsou ukládány v pořadí (1,1,...), (2,1,...), (3,1,...) atd. [3] [4]

! deklarace polí
integer ia(10)                      ! celočíselné 1D 10prvkové statické pole s prvky ia(1)..ia(10)
integer ib(10,10)                   ! 2D 100prvkové pole, prvky ib(1,1)..ib(10,10)
real a(0:10),b(-2:2,1:3)            ! reálný vektor a reálná matice s explicitními dolními mezemi
complex,dimension(5) :: c1,c2       ! dva komplexní vektory
real(8),allocatable :: d(:),e(:,:)  ! alokovatelný vektor a alokovatelná matice
real(8),pointer :: pv(:),pm(:,:)    ! ukazatelový vektor a ukazatelová matice
real(8) :: z(10)=0.                 ! statické pole se všemi prvky inicializovanými skalárem
integer,parameter :: ic(3)=[1,2,3]  ! konstantní pole inicializované konstruktorem
[1]Deklarační meze statických polí musí být konstanty (literály nebo symbolické konstanty), nikoliv proměnné, byť inicializované:
! statické deklarace se symbolickými konstantami
integer,parameter :: nmax=10; real(8) x(nmax) ! mez jako symbolická konstanta
integer           :: nmax=10; real(8) x(nmax) ! NELZE - mez jako inicializovaná proměnná
[2]Konstruktor pole je nepojmenovaným polem, obvykle užívaným k inicializaci pojmenovaného pole. Může obsahovat buď výčet prvků nebo cyklický seznam (expression-list,var=lbound,ubound,stride) nebo obojí. Konstruktor pole je vždy vektorem, bez ohledu na případné vnořené konstruktory, pro inicializaci vícerozměrných polí je konstruktor třeba přetvarovat funkcí reshape. Všechny prvky konstruktoru musí být téhož typu i podtypu, řetězce i téže délky, anebo se výsledný typ nebo délka musí specifikovat explicitně – nelze [0,1.] ani ['a','bc'], lze však [real(8) :: 0,1.], [character(3) :: 'a','bc']. Starší syntaxe pro konstruktor pole je (/.../), novější [...].
! inicializace polí pomocí konstruktorů
integer :: vec(100)=[(i,i=1,100)]   ! inicializace vektoru (konstruktor vždy vektorem)
integer :: mat(2,2)=transpose(reshape([1,2, 3,4],[2,2]))
         ! inicializace matice (transpose tehdy, jsou-li prvky konstruktoru zadány po řádcích)
character(10) :: obdobi(4)=[character(10) :: 'jaro','léto','podzim','zima']
         ! explicitní deklarace délky prvků v řetězcovém konstruktoru
[3]V důsledku řazení prvků matic po sloupcích příkaz print *,mat vede k výpisu sloupců matice na výstupní řádek. Pro rozumný výpis matice po řádcích je tak třeba ji transponovat a výpis vhodně rozřádkovat, nebo vypisovat v cyklu po řádcích:
! výpis matice po řádcích
integer,parameter :: nmax=5
character(10) :: format='(5i4)'
integer :: mat(nmax,nmax)=transpose(reshape([(i,i=1,nmax**2)],[nmax,nmax]))
                                    ! prvky konstruktoru chápány po řádcích, proto transpose
write (format,'(a,i4,a)') '(',nmax,'i4)'  ! obecnější řešení přípravy formátového řetězce
print format,transpose(mat)         ! výpis matice po řádcích
do i=1,nmax; print format,mat(i,:); enddo
                                    ! totéž v cyklu po řádcích s užitím sekce polí
[4]Průchod maticemi po sloupcích vychází výrazně rychleji než po řádcích, což ovšem může být zahlazeno optimalizací. Následující kód prochází (a sčítá) 800MB-ové matice po sloupcích a po řádcích. (Pro srovnání je uveden i ekvivalentní maticový zápis, komentovaný o něco níže. Jeho efektivita je mírně horší než průchod po sloupcích pomocí cyklů, zřejmě kvůli nezbytné alokaci paměti pro vyčíslení celé pravé strany před přesunem nalevo.) Výsledné časy maticově/po sloupcích/po řádcích: gfortran -O3 0.19/0.15/0.55 s, g95 -O3 0.20/0.15/0.78 s, ifort -O1 0.19/0.14/0.78 s, ifort -O2 0.24/0.15/0.15 s (varianta po řádcích optimalizována), pgfortran -O1 0.27/0.22/1.36 s, pgfortran -O2 0.19/0.16/0.16 s (varianta po řádcích optimalizována).
! průchod maticí po sloupcích a po řádcích
integer,parameter :: nmax=10000
real(8),dimension(nmax,nmax) :: a,b,c
print *,'maticove...';     c=a+b
print *,'po sloupcich...'; do j=1,nmax; do i=1,nmax; c(i,j)=a(i,j)+b(i,j); enddo; enddo
print *,'po radcich...';   do i=1,nmax; do j=1,nmax; c(i,j)=a(i,j)+b(i,j); enddo; enddo

(Array inquiry functions) Velikost, tvar a meze pole lze zjistit dotazovacími funkcemi size, shape, lbound a ubound. Funkce size vrací vždy skalár, buď celkovou velikost pole nebo (s druhým argumentem) velikost v konkrétní dimenzi. Funkce shape vrací vždy integer vektor s prvky udávajícími velikost v jednotlivých dimenzích. Poněkud zrádné může být, že funkce lbound a ubound, mají-li pole jako jediný argument, vracejí hodnoty jeho mezí jako integer vektor. Pro 1D pole tak vracejí jednoprvkový vektor, který nelze přímočaře použít např. v mezích indexovaného cyklu. Nelze tedy psát do i=lbound(ia),ubound(ia), ale je třeba použít variantu funkcí s druhým argumentem specifikujím, o kterou dimenzi jde; pak funkce vracejí příslušnou mez jako skalár: do i=lbound(ia,1),ubound(ia,1). Častou běhovou chybou bývá překročení mezí polí; překladače umožňují zabudovat do programů kontrolu: [5].

! dotazovací funkce
print *,size(a),size(b),size(b,1),size(b,2) ! velikost polí celkem a po dimenzích: 11 15 5 3
print *,shape(a),shape(b)          ! tvar pole jako vektor velikostí po dimenzích: [11] [5 3]
print *,ubound(b),ubound(b,1),ubound(b,2)   ! horní mez pole vektorově i skalárně: [2 3] 2 3
[5]Volby překladačů pro kontrolu přetékání indexů: gfortran -fcheck=bounds, g95 -fbounds-check, Intel -check bounds, Portland -Mbounds.

(Array sections) Sekce neboli řezy pole vznikají výběrem prvků z pole, jsou to také pole. Sekci pole může popisovat indexový triplet imin:imax:stride nebo při kroku +1 indexový dublet imin:imax; shoduje-li se imin, resp. imax s dolní, resp. horní mezí pole, může se vynechat: ia(:5) pro první polovinu pole ia, ia(2::2) pro sudé prvky pole ia, ib(6:,6:) pro pravý dolní blok matice ib. Pravidelné krokování indexového tripletu může být překonáno vektorovým indexem neboli integer polem indexů, zde s totožným obsahem jako předchozí příklady: ia([1,2,3,4,5]), ia([2,4,6,8,10]), ib([6,7,8,9,10],[6,7,8,9,10]). Použití tripletu, dubletu nebo vektorového indexu nesnižuje počet dimenzí sekce, použití prostého indexu ano – pro matice je běžná algebraická terminologie: sekce ib(i,:) se nazývá i-tým řádkem, sekce ib(:,j) j-tým sloupcem.

(Array language) Pole (i sekce polí) se v četných případech mohou objevit tamtéž, kde prvky pole. Přiřazovací příkaz snáší podobná pole (conformable arrays, tj. pole téhož tvaru) na obou svých stranách (elemental assignment neboli prvkové přiřazení, přiřazuje se prvek po prvku), pole mohou být operandy ve výrazech (elemental operations, operátor se aplikuje na příslušné dvojice prvků) i argumenty běžných vnitřních funkcí, které se pak nazývají prvkové funkce (elemental functions) a vracejí místo skaláru pole téhož tvaru jako argument. Na pravé straně prvkového přiřazení se může objevit skalár, jeden z operandů prvkového výrazu může být skalár, prvkové funkce o více argumentech mohou mít některé z argumentů skalární. Na levé straně přiřazovacího příkazu se nemůže objevit sekce pole s vektorovým indexem. Prvková přiřazení jsou syntakticky úsporná, mohou však být nehospodárná paměťově a potažmo i výkonem – nejprve se vyčíslí všechny prvky pravé strany v dočasně alokované paměti a až poté se provede jejich přiřazení do příslušných prvků pole na levé straně [6]. Kontrola podobnosti polí v překladačích: [7].

! prvková přiřazení
ia=0            ! přiřazení skaláru do všech prvků pole
ia=[(i,i=-4,5)] ! přiřazení mezi poli, prvek po prvku
ib(1,:)=ia      ! přiřazení vektoru do prvního řádku matice

! výpisy polí, cyklických seznamů a sekcí
print *,ia; print *,(ia(i),i=1,10); print *,ia(1:10) ! třikrát totožný výpis

! prvkové výrazy
print *,ia**2   ! umocnění všech prvků pole
print *,ia*ia   ! totéž, násobí se odpovídající si páry prvků (maticové násobení se řekne jinak)

! prvkové funkce
print *,abs(ia) ! funkce aplikovaná prvek po prvku
print *,merge(ia,-ia,ia>0) ! totéž pomocí funkce pro slučování podle masky
[6]Ukázka možné interpretace prvkového přiřazení. Výkonový rozdíl prvkového přiřazení a skalárních přiřazení v cyklu je vyčíslen výše u poznámky o průchodu maticemi.
! Interpretace prvkového přiřazení
real(8),dimension(nmax) :: a,b,c
real(8),allocatable :: temp(:)
! c=a+b neodpovídá jednomu cyklu ...
do i=1,nmax; c(i)=a(i)+b(i); enddo
! ... ale dvěma cyklům (nikoliv nutně sekvenčním)
allocate (temp(size(c)))
do i=1,nmax; temp(i)=a(i)+b(i); enddo
do i=1,nmax; c(i)=temp(i); enddo
deallocate (temp)
[7]Prvková přiřazení, prvkové výrazy i prvkové vnitřní funkce vyžadují, aby všechna pole v nich byla podobná (neboli téhož tvaru). Překladače zajišťují kontrolu podobnosti polí jen v omezené míře – podobnost statických polí nebo sekcí statické velikosti testována bývá, podobnost polí, jejichž velikost vyplyne až za chodu programu, spíše ne. Čestnou výjimkou je gfortran, který s volbou -fcheck=bounds překládá programy ověřující podobnost jak polí, tak sekcí pole: sekvence real a(10); i=5; a(:i)=a(i:) vyvolá běhovou chybu Array bound mismatch for dimension 1 of array ‘a’ (5/6).

Pro pole je definována sada funkcí pro frekventované dotazy (existence a počet prvků splňujících předepsanou podmínku, součet a součin prvků, nalezení hodnot a poloh extrémů, skalární a maticový součin [8] aj.).

! transformační funkce pro různé souhrny z polí, multiplikační funkce
print *,all(ia>0)     ! platí relace pro všechny prvky? zde: F
print *,any(ia>0)     ! platí relace pro alespoň jeden prvek? zde: T
print *,count(ia>0)   ! pro kolik prvků platí relace? zde: 5
print *,minval(ia),minloc(ia) ! hodnota a poloha minima: -4 [1]
print *,maxval(ia),maxloc(ia) ! hodnota a poloha maxima: 5 [10]
print *,sum([1,2,3]),product([1,2,3]) ! součet a součin prvků: 6 6
print *,dot_product(ia,ia),sum(ia*ia) ! skalární součin vektorů dvěma způsoby
print *,matmul(ib,ib) ! maticový součin dvou matic
print *,matmul(ia,ib),matmul(ib,ia)   ! vektor krát matice, matice krát vektor

! manipulační funkce
integer m(2,2); m=reshape([1,0,0,1],shape(m)) ! přetvarování 1D konstruktoru na matici
m=transpose(m)        ! transpozice matice
pack
unpack
m=spread([1,2],dim=2,ncopies=2) ! vytvoření matice replikováním vektoru po sloupcích
[8]Maticové násobení pomocí matmul může co do rychlosti provedení vyjít efektivněji než ekvivalentní zápis pomocí cyklů, překladače však o kvalitní optimalizaci spíše neusilují a ponechávají ji na knihovním řešení. Např. Intel nahradí standardní kód pro matmul optimalizovanou rutinou až s volbou překladače -qopt-matmul, gfortran umožňuje volbou -fexternal-blas konvertovat matmul na ekvivalentní volání rutiny z knihovny BLAS, která ovšem musí být výslovně připojena.

(Mask. Where. Forall) V řadě situací lze aplikovat masku neboli logické pole, jehož pozitivní prvky vyznačují, na které prvky zpracovávaného pole omezit prováděnou akci. Maska je argumentem už uvedených funkcí all, any, count, merge a je volitelným argumentem i ostatních transformačních funkcí. Podle masky lze větvit v příkazové konstrukci where (mask); array-assignments-true; elsewhere; array-assignments-false; end where a maskou lze omezit přiřazení v nesekvenčním cyklu forall (i=imin:imax:stride, scalar-mask); body; end forall, kde tělo cyklu může obsahovat přiřazení, příkazy where a vnořené forall. V cyklu forall se stejně jako v prvkovém přiřazení nejprve vyčíslí (v libovolném pořadí) všechny prvky pravé strany, až poté se provede jejich přiřazení do příslušných prvků pole na levé straně.

! maska v transformačních funkcích
print *,sum(ia,mask=ia>0)       ! součet kladných prvků pole: 15
print *,maxval(ia,mask=ia/=maxval(ia)) ! nalezení druhé největší hodnoty: 4

! příkazová konstrukce pro přiřazení podle masky
where (ia<0); ia=-ia; end where ! podobné jako merge(-ia,ia,ia<0), ale bez nadbytečné negativní větve
where (a/=0); a=1/a; elsewhere; a=huge(a); end where
                   ! podobné jako s merge, kde by se však argument 1/a počítal i pro 0 ve jmenovateli
! nesekvenční cyklus
forall (i=lbound(ia,1):ubound(ia,1))
  ia(i)=abs(ia(i))              ! totéž, co ia=abs(ia)
end forall
forall (i=lbound(ia,1):ubound(ia,1), ia(i)<0)
  ia(i)=-ia(i)                  ! totéž, co where (ia<0) ia=-ia
end forall
forall (i=lbound(ib,1):ubound(ib,1), j=lbound(ib,2):ubound(ib,2))
  ib(i,j)=dot_product(ib(i,:),ib(:,j)) ! totéž co ib=matmul(ib,ib)
end forall

(Statická a dynamická pole) Pro dynamickou alokaci polí nabízí Fortran dvě varianty: alokovatelná pole (deklarační atribut allocatable), která jsou stejně jako statická pole uložena v paměti souvisle a mohou být dealokována automaticky, a obecnější ukazatelová pole (deklarační atribut pointer), která mohou odkazovat na složitější paměťové rozlohy a podobně jako jiné ukazatele se automaticky nedealokují. Preference alokovatelných polí před ukazatelovými může vést k vyšší efektivitě a bezpečnosti, s ukazatelovými poli toho lze dělat více. Statická či dynamická alokace není na překážku podobnosti polí a nevadí ani při předávání polí jako argumentů. Úspěch dynamické alokace či dealokace lze bezprostředně testovat pomocí atributu stat příkazů allocate a deallocate, alokační status alokovatelných polí lze zjistit dotazovací funkcí allocated. Přealokovat alokovatelné pole lze po jeho dealokaci nebo přenosem alokace pomocí podprogramu move_alloc nebo novým přiřazením (to bývá podmíněno volbou překladače – GNU: default, Intel: -assume realloc_lhs, PGI: -Mallocatable=03, g95: nelze):

! alokovatelná pole
real(8),allocatable :: a(:),temp(:),b(:,:) ! alokovatelné vektory a alokovatelná matice
allocate (a(5)); a=1.                   ! alokace a inicializace alokovatelného vektoru
print *,allocated(a),allocated(b)       ! test alokovanosti alokovatelných polí: T F
allocate (temp(0:10)); temp=0.; temp(1:size(a))=a; call move_alloc(temp,a) ! realokace pole a
print *,allocated(a),allocated(temp)    ! test alokovanosti alokovatelných polí: T F
print *,size(a); a=pack(a,a>0); print *,lbound(a,1),size(a) ! realokace pole přiřazením: 11 1 5
if (allocated(a)) deallocate (a)        ! podmíněná dealokace
deallocate (b,stat=ie); if (ie/=0) print *,'chyba' ! bezpečná dealokace s nastavením kódu chyby

Ukazatelová pole se alokují a dealokují stejně jako alokovatelná pole pomocí příkazů allocate a deallocate, jejich status se zjišťuje dotazovací funkcí pro ukazatele associated. Ukazatelová pole mohou mířit i na jiná pole nebo sekce polí, díky čemuž nemusejí být v paměti uložena spojitě. Pole, která se stanou cílem ukazatelového pole, musí být explicitně deklarována atributem target. Při přesměrování ukazatelového pole ani při ukončení jeho platnosti se neprovádí automatická dealokace cíle, je proto třeba čelit únikům paměti (memory leaks). (Programy přeložené g95 oznamují po ukončení únik paměti automaticky.)

! ukazatelová pole
real(8),pointer :: pa(:),pb(:,:)        ! ukazatelový vektor a ukazatelová matice
real(8),target :: a(1:10),b(10,10)      ! statická pole, možné cíle ukazatelů
allocate (pa(5),pb(-2:2,-2:2))          ! alokace ukazatelových polí
print *,associated(pa),associated(pb)   ! test asociovanosti ukazatelových polí: T T
deallocate (pa,pb,stat=ie)              ! bezpečná dealokace
print *,associated(pa),associated(pb),ie ! test asociovanosti ukazatelových polí: F F 0
pa=>a; print *,associated(pa),size(pa)  ! přesměrování na vektor: T 10
pa=>b(1,:); print *,size(pa)            ! přesměrování na řádek matice (nespojitý cíl): 10
allocate (pa(5)); pa=>a                 ! přesměrování alokovaného ukazatelového pole: memory leak

Tím výbava moderního Fortranu pro práci s poli nekončí. V části o procedurách bude řeč o funkcích vracejících pole, o deklaracích lokálních polí a o zacházení s poli v argumentech procedur.

Volby překladačů

Shrnujeme vybrané volby pro řádkový překlad čtyřmi překladači: GNU, g95, Intel a Portland Fortran. Volby jsou platné v Linuxu i Windows s výjimkou Intelu, jehož syntaxe pro Windows se odchyluje. Ověřeno s následujícími verzemi: gfortran 4.8.4, g95 0.93/0.94, ifort 15.0.3, pgfortran neboli pgf95 15.10.

akce gfortran g95 ifort Linux ifort Windows pgfortran
nápověda man gfortran, man gcc, -v –help G95Manual.pdf, –help man ifort, -help -help man, -help, -show
výstupní soubor -o outfile (default: a.out) -o outfile -o outfile -o outfile, -exe:outfile -o outfile
min. optimalizace -O0 (default) -O0 (default) -O0 -Od -O0, -O1 (default)
optimalizace -O2 -O2 -O2 (default) -O2 (default) -O2
max. optimalizace -Ofast -march=native -O3 -fast, -Ofast -xHost -fast, -O3 -QxHost -fast, -O4
jen překlad -c -c -c -c -c
pro debugger -g, -ggdb -g, -ggdb -g, -debug, -traceback -Z7, -debug, -traceback -g, -traceback
autoparalelizace (ne) (ne) -parallel, -guide -Qparallel, -Qguide -Mconcur, -Mvect
OpenMP -fopenmp (ne) -qopenmp -Qopenmp -mp
coarrays -fcoarray=single|caf_mpi (default) -coarray=single|shared -Qcoarray:single|shared (ne)
koprocesory -fopenacc (ne) -mmic, -qoffload   -acc, -ta, -Mcuda
BLAS a LAPACK -fexternal-blas, -lblas, -llapack -lblas, -llapack -qopt-matmul, -mkl -Qopt-matmul, -Qmkl -lacml
statická data 2+ GB -mcmodel=medium   -mcmodel=medium   -mcmodel=medium
šetření zásobníku -fno-automatic   -heap-arrays, -save -heap-arrays, -Qsave, -F n -Mnostack_arrays, -Msave
zachycení real výjimek -ffpe-trap= overflow,invalid,zero proměnné G95_FPU_* -fpe0, -init=snan -fpe:0, -Qinit=snan -Ktrap=ovf,inv,divz | fp
kontrola chyb za běhu -fcheck=all   -C, -check all -C, -check:all  
kontrola mezí polí -fcheck=bounds -fbounds-check -CB, -check bounds -CB, -check:bounds -C, -Mbounds
kontrola neinic. prom. -Wuninitialized (nefunkční) -Wunset-vars -CU, -check uninit -CU, -check:uninit  
default inicializace -finit-local-zero, -finit-real= -fzero, -finteger=, -freal= -zero, -init= -Qzero, -Qinit=  
default real přesnost -fdefault-real-8, -freal-4-real-8 -r8 -r8, -r16 -4R8, -4R16 -Mr8, -r8
implicit none -fimplicit-none -fimplicit-none -warn declarations -warn:declarations -Mdclchk
prodloužení řádku -ffree-line-length-n -ffree-line-length-huge -extend-source -extend-source -Mextend
méně informací -w -w -w -w, -nologo -w, -silent
více informací -Wall, -pedantic -Wall, -pedantic -warn -warn -Minform=inform, -Minfo
shoda s normou -std=f95|f2003|f2008 -std=f95|f2003 -stand f95|f03|f08 -stand:f95|f03|f08 -Mstandard
  • Optimization. V gfortranu a g95, na rozdíl od Intelu a Portlandu, je třeba optimalizaci zapnout explicitně. Počítání s vyššími hladinami optimalizace bývá rychlejší, výsledky ale mohou být odlišné.
  • Thread paralelization. Explicitní vláknová (OpenMP) paralelizace je běžně dostupná, s výjimkou g95. Intel a Portland nabízejí volby i pro OpenMP autoparalelizaci, gfortran automaticky neparalelizuje. Explicitní i automatická OpenMP paralelizace může vyžadovat nastavení některých proměnných prostředí (OMP_NUM_THREADS aj.).
  • Coarrays. Některé implementace kooperativních polí vyžadují připojení externích knihoven (gfortran: libcaf_mpi).
  • Coprocessors. Intel umožňuje (OpenMP a MPI) programování vlastního MIC neboli Xeon Phi koprocesoru, Portland poskytuje podporu grafickým procesorům (GPU) Nvidia a AMD, mj. formou direktiv OpenACC, jejichž implementaci připravuje i gfortran.
  • Linear algebra. Algoritmy lineární algebry jsou obecně pokryty knihovnami BLAS a LAPACK, zahrnutými též v knihovnách Intel MKL (přibaleno k Intelu) a AMD ACML (přibaleno k Portlandu).
  • Memory limits. Staticky deklarovaná pole jsou překladači v Linuxu omezena hranicí 2 GB, kterou lze překročit volbou -mcmodel=medium. Přetečení zásobníku (stack) lze čelit volbami pro přednostní vytváření polí na hromadě (heap) místo v zásobníku (Intel: -heap-arrays, Portland: -Mnostack_arrays) nebo explicitním zvětšením zásobníku (gfortran a g95: -Wl,--stack=0x1000000, Intel pro Windows: -F n). V linuxovských shellech se lze potkat s limitem pro velikost zásobníku, který se uplatňuje i na fortranské programy; aktuální velikost limitů v bashi se zjistí příkazem ulimit -a, obvykle pak následuje ulimit -s unlimited. Potřeba nastavit velikost zásobníku explicitně se často vynoří po aktivaci OpenMP paralelizace (proměnná OMP_STACKSIZE aj.).
  • Floating-point exceptions. Překladače běžně generují kód, který nezachycuje tzv. floating-point výjimky – přetečení do Infinity, matematicky nedefinované operace s výsledkem NaN (Not-a-Number) a speciální dělení nulou. Volbami překladače nebo proměnnými prostředí (g95: G95_FPU_OVERFLOW=1, také G95_FPU_INVALID, G95_FPU_ZERODIV) lze výjimky zachycovat a program při jejich výskytu ukončit.