Básico & Estrutura do Programa
Estrutura do Programa & Hello World
Todo programa Fortran começa com 'program NAME' e termina com 'end program NAME'. 'implicit none' é OBRIGATÓRIO em Fortran moderno — força a declaração explícita de todas as variáveis (sem ele, o Fortran usa tipagem implícita onde variáveis começando com i-n são integer, outras real, o que é uma grande fonte de bugs). O bloco 'contains' separa código executável de procedimentos internos (subroutines/functions definidos dentro do programa). Comentários começam com '!'. Free source form (Fortran 90+) usa extensão .f90; colunas não importam. Compile com gfortran/ifort.
program hello
! A complete Fortran program structure
implicit none
! declarations go here
integer :: status = 0
print *, "Hello, World!" ! list-directed output to stdout
! executable statements
call do_work(status)
print *, "Exit status: ", status
contains
subroutine do_work(st)
integer, intent(out) :: st
st = 0
print *, "Working..."
end subroutine do_work
end program hello
! Compile: gfortran hello.f90 -o hello
! Run: ./helloVariáveis & Tipos Intrínsecos
Fortran tem 5 tipos intrínsecos: integer, real, complex, character, logical. 'kind' seleciona precisão/tamanho — use kind=8 para 64-bit (ou melhor, use selected_real_kind/iso_fortran_env para portabilidade). Literais real precisam de um sufixo kind: 3.14_8 (não apenas 3.14). Double precision é a sintaxe legada para real(kind=8). Literais complex usam a forma (real, imag). Valores logical são .true. / .false. (com pontos). Strings character têm um 'len' fixo a menos que declaradas com len=: e allocatable (deferred-length, Fortran 2003+). Sempre inicialize com o sufixo kind correspondente para evitar perda silenciosa de precisão.
program variables
implicit none
! Integer types
integer :: count = 0
integer(kind=8) :: big = 9223372036854775807_8 ! 64-bit
! Real types
real :: x = 3.14 ! default (often 32-bit)
real(kind=8) :: y = 2.718281828459045_8 ! double precision
double precision :: z = 1.0d0
! Complex
complex :: c = (1.0, 2.0) ! 1 + 2i
complex(kind=8) :: cw = (1.0_8, 2.0_8)
! Character
character(len=20) :: name = "Alice"
character(len=:), allocatable :: flexible ! deferred length
! Logical
logical :: flag = .true.
! Print all
print *, count, big
print *, x, y, z
print *, c, cw
print *, name, flag
end program variablesConstantes & Parâmetros
Constantes usam o atributo 'parameter' e devem ser inicializadas na declaração. Elas não podem ser modificadas — o compilador pode otimizá-las e inseri-las inline. Use SCREAMING_SNAKE_CASE por convenção. character(*) significa 'pegue o comprimento do inicializador' (útil para constantes de string). Parâmetros são comumente usados para tamanhos de arrays, constantes físicas e códigos integer semelhantes a enum. Fortran 2003+ também tem tipos ENUM próprios, mas integers parameter permanecem a escolha idiomática. Parâmetros podem ser usados em declarações de dimensão de array e outros contextos de expressão constante.
program constants
implicit none
! Named constants via 'parameter' attribute
integer, parameter :: MAX_SIZE = 100
real, parameter :: PI = 3.14159265
real, parameter :: E = 2.718281828
character(*), parameter :: APP_NAME = "MyApp"
! Using parameters
real :: arr(MAX_SIZE)
arr = 0.0
print *, APP_NAME, " size=", MAX_SIZE
print *, "Circumference: ", 2.0 * PI * 5.0
! Enum-like via parameter
integer, parameter :: SUNDAY = 1, MONDAY = 2, TUESDAY = 3
integer :: day = MONDAY
print *, "Day code: ", day
end program constantsOperadores & Expressões
Operadores do Fortran: aritméticos (+ - * / **), com ** para exponenciação (exclusivo do Fortran). Divisão integer trunca em direção a zero — use real(a)/b para divisão verdadeira. Duas sintaxes relacionais: moderna (< > == /= <= >=) e legada (.lt. .gt. .eq. .ne. .le. .ge.). Lógicos: .and. .or. .not. .eqv. (equivalência) .neqv. (ou exclusivo). Concatenação de strings usa //; trim() remove espaços à direita (Fortran preenche strings de comprimento fixo com espaços). mod vs modulo: mod segue sinal de divisão truncada, modulo segue divisão com floor — eles diferem para operandos negativos.
program operators
implicit none
integer :: a = 17, b = 5
real :: x = 2.0
! Arithmetic
print *, a + b, a - b, a * b ! 22 12 85
print *, a / b ! 3 (integer division!)
print *, real(a) / b ! 3.4 (cast to real)
print *, a ** 2 ! 289 (exponentiation)
print *, mod(a, b) ! 2 (modulo)
print *, modulo(a, b) ! 2 (differs for negatives)
! Relational (both forms work)
print *, a > b, a < b ! T F
print *, a .gt. b, a .lt. b ! T F (old form)
print *, a == b, a /= b ! F T
! Logical
print *, (a > 0) .and. (b > 0) ! T
print *, (a > 0) .or. (b < 0) ! T
print *, .not. (a > 0) ! F
print *, (a > 0) .eqv. (b > 0) ! T (equivalence)
! String concatenation
character(10) :: s1 = "Hello", s2 = "World"
print *, trim(s1) // " " // trim(s2) ! Hello World
end program operatorsFunções Intrínsecas & Matemática
Fortran tem um conjunto rico de funções intrínsecas (built-in). Matemática: abs, sqrt, exp, log (natural), log10, sin/cos/tan/asin/acos/atan/atan2, sinh/cosh/tanh. Arredondamento: int (trunca), nint (mais próximo), floor, ceiling. Conversão: real(), int(), cmplx(). Inquiry: size, shape, huge (valor máximo), tiny (mínimo positivo), kind. Todas as funções trigonométricas recebem radianos. atan2(y, x) retorna o ângulo no quadrante correto (diferente de atan). Use huge/tiny para verificar limites de intervalo. As intrínsecas são elementais — elas funcionam em arrays elemento a elemento automaticamente.
program intrinsics
implicit none
real :: x = -3.7, y = 2.5
! Math functions
print *, abs(x) ! 3.7
print *, sqrt(2.0) ! 1.414...
print *, exp(1.0) ! 2.718... (e^x)
print *, log(2.0) ! 0.693... (natural log)
print *, log10(1000.0) ! 3.0
print *, sin(3.14159/2) ! 1.0
print *, cos(0.0) ! 1.0
print *, atan2(1.0,1.0) ! 0.785... (pi/4)
! Rounding
print *, int(x) ! -3 (truncate toward zero)
print *, nint(x) ! -4 (nearest integer)
print *, floor(x) ! -4 (toward -inf)
print *, ceiling(x) ! -3 (toward +inf)
print *, abs(x), max(x, y), min(x, y) ! 3.7 2.5 -3.7
! Type conversion
print *, real(5) ! 5.0
print *, int(3.9) ! 3
! Inquiry
real :: arr(10)
print *, size(arr) ! 10
print *, huge(1) ! 2147483647
print *, tiny(1.0) ! smallest positive real
end program intrinsicsFluxo de Controle
If...Then...Else
Block IF: 'if (cond) then ... else if (cond) then ... else ... end if'. Cada branch precisa de 'then' (exceto o else final). Logical IF é uma linha única: 'if (cond) statement' (sem 'then'/'end if'). Condições usam operadores relacionais (< > == /= <= >= ou .lt. .gt. .eq. .ne. .le. .ge.) combinados com .and. .or. .not. 'stop' termina o programa (opcionalmente com uma mensagem/código). O arithmetic IF (if (x) label1, label2, label3) foi removido no Fortran 2018 — nunca o use. Sempre use 'implicit none' para que variáveis não declaradas sejam capturadas.
program if_demo
implicit none
integer :: score = 85
character(1) :: grade
! Multi-branch if/else if/else
if (score >= 90) then
grade = 'A'
else if (score >= 80) then
grade = 'B'
else if (score >= 70) then
grade = 'C'
else if (score >= 60) then
grade = 'D'
else
grade = 'F'
end if
print *, "Score ", score, " -> Grade ", grade
! Logical if (single statement, no 'then')
if (score < 0 .or. score > 100) stop "Invalid score"
! Arithmetic if (OBSOLETE - avoid)
! if (x) 10, 20, 30 ! jump to label based on sign
end program if_demoSelect Case (Switch)
select case é a declaração switch do Fortran. Cases podem ser valores únicos (case (3)), listas (case (1, 3, 5)) ou intervalos (case (4:5) significa 4 a 5 inclusive). case default é o fallback. Diferente do C, NÃO há fall-through — cada branch é independente e apenas um roda. Funciona com tipos integer, character e logical (NÃO real). Intervalos de character usam ordenação ASCII ('A':'Z'). Para comparações de ponto flutuante, use if/else. select case é mais eficiente do que longas cadeias if/else if para dispatch de integer/char (o compilador pode usar jump tables).
program case_demo
implicit none
integer :: day = 3
character(1) :: ch = 'A'
character(10) :: day_name
! Integer select case
select case (day)
case (1)
day_name = "Monday"
case (2)
day_name = "Tuesday"
case (3)
day_name = "Wednesday"
case (4:5)
day_name = "Thu/Fri"
case (6:7)
day_name = "Weekend"
case default
day_name = "Invalid"
end select
print *, day_name
! Character select case (case-insensitive via pre-upper)
select case (ch)
case ('A':'Z')
print *, "Uppercase letter"
case ('a':'z')
print *, "Lowercase letter"
case ('0':'9')
print *, "Digit"
case default
print *, "Other"
end select
! Logical select case
select case (day > 5)
case (.true.)
print *, "Weekend!"
case (.false.)
print *, "Weekday"
end select
end program case_demoLoops Do (Contados)
Loop DO contado: 'do var = start, end, step' (step padrão é 1). O loop roda enquanto var <= end (para step positivo) ou var >= end (para step negativo). var é incrementado APÓS cada iteração. Construtos implied-do [(expr, var=start,end)] são poderosos para inicialização de arrays e listas de I/O. Loops nomeados (outer: do ... end do outer) permitem direcionar cycle/exit a um nível específico de aninhamento. A variável de loop é automaticamente definida; no Fortran ela retém seu valor final após o loop. Evite modificar a variável de loop dentro do corpo do loop.
program do_loops
implicit none
integer :: i, j, total
! Basic counted loop: do var = start, end [, step]
do i = 1, 5
print *, i ! 1 2 3 4 5
end do
! With step
do i = 10, 1, -1 ! countdown
print *, i
end do
do i = 0, 100, 25 ! 0 25 50 75 100
print *, i
end do
! Implied-do (inline, for array init / I/O)
integer :: arr(5) = [(i**2, i=1,5)] ! 1 4 9 16 25
print *, arr
print *, (i, i=1,3) ! 1 2 3
! Nested loops with labels (for cycle/exit targeting)
total = 0
outer: do i = 1, 3
inner: do j = 1, 3
total = total + i*j
end do inner
end do outer
print *, "Total: ", total
end program do_loopsDo While & Loops Infinitos
do while (cond) ... end do é um loop pré-teste (verifica a condição antes de cada iteração; pode rodar zero vezes). Para comportamento pós-teste, use do ... if (cond) exit ... end do. O 'do ... end do' puro é um loop infinito — você DEVE ter uma declaração exit (caso contrário, infinito). 'exit' sai do loop mais interno (ou de um loop nomeado). Loops nomeados (factorial_loop:) permitem que exit direcione um loop externo. Use do while quando a contagem de iterações é desconhecida e depende de uma condição; use do contado quando a contagem é conhecida de antemão.
program while_demo
implicit none
integer :: n, count
real :: x, sum
! do while: pre-test loop
n = 1024
count = 0
do while (n > 1)
n = n / 2
count = count + 1
end do
print *, "log2(1024) = ", count ! 10
! Infinite loop with exit
sum = 0.0
do
read(*, *) x
if (x < 0) exit ! leave loop
sum = sum + x
end do
print *, "Sum: ", sum
! do ... end do with conditional exit
n = 1
factorial_loop: do
if (n > 10) exit factorial_loop
print *, n, factorial(n)
n = n + 1
end do factorial_loop
contains
recursive function factorial(n) result(f)
integer, intent(in) :: n
integer :: f
if (n <= 1) then
f = 1
else
f = n * factorial(n-1)
end if
end function factorial
end program while_demoCycle, Exit & Controle de Loop
cycle pula o resto da iteração atual e pula para a próxima (como 'continue' em C/Python). exit sai do loop completamente (como 'break'). Ambos têm como alvo o loop mais interno por padrão, mas com loops nomeados (search: do ... end do search) você pode direcionar um loop externo: 'exit search' ou 'cycle search'. Isso é essencial para sair de loops aninhados de forma limpa. Use cycle para filtragem (pular iterações indesejadas) e exit para terminação antecipada (busca encontrada, erro detectado). Loops nomeados tornam o fluxo de controle aninhado explícito e legível.
program loop_control
implicit none
integer :: i, j
! cycle: skip to next iteration (like 'continue' in C)
do i = 1, 10
if (mod(i, 2) == 0) cycle ! skip even numbers
print *, i ! 1 3 5 7 9
end do
! exit: break out of loop (like 'break' in C)
do i = 1, 100
if (i * i > 50) then
print *, "Stopped at i=", i
exit
end if
end do
! Named loops: cycle/exit can target outer loops
search: do i = 1, 5
do j = 1, 5
if (i + j == 7) then
print *, "Found: ", i, "+", j, "= 7"
exit search ! break out of OUTER loop
end if
end do
end do search
! Early exit from a search
integer :: arr(10) = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]
do i = 1, size(arr)
if (arr(i) == 9) then
print *, "Found 9 at index ", i
exit
end if
end do
end program loop_controlArrays & Operações com Vetores
Declaração & Inicialização de Arrays
Arrays Fortran são 1-indexed por padrão (limite inferior = 1), mas você pode especificar um limite inferior personalizado: a(0:4) tem índices 0..4. Arrays multidimensionais usam ordenação (rows, cols) — armazenamento column-major (primeiro índice varia mais rápido na memória). Inicialize com construtores de array [1,2,3] ou implied-do [(expr, i=start,end)]. reshape preenche um array multi-dimensional a partir de uma lista 1D. size() retorna o total de elementos; lbound/ubound retornam limites inferior/superior. shape() retorna a forma como um array 1D. Arrays são 'whole-array' — você pode atribuir e operar neles sem loops explícitos.
program array_decl
implicit none
! Declaration with dimension
integer :: a(5) ! 1D, indices 1..5
integer :: b(0:4) ! 1D, indices 0..4 (custom lower bound)
real :: c(3, 4) ! 2D, 3 rows x 4 cols
real, dimension(10) :: d ! using dimension attribute
! Initialization at declaration
integer :: x(5) = [1, 2, 3, 4, 5]
integer :: y(5) = [(i*2, i=1,5)] ! implied-do: 2 4 6 8 10
integer :: z(5) = 0 ! all zeros
real :: m(2,2) = reshape([1,2,3,4], [2,2])
! Allocation later
print *, size(x), lbound(x), ubound(x) ! 5 1 5
print *, size(c, dim=1) ! 3 (rows)
print *, size(c, dim=2) ! 4 (cols)
! Array of characters
character(10) :: names(3) = ["Alice", "Bob", "Carol"]
print *, names(2) ! Bob
end program array_declSeções de Array & Subscripts de Vetor
Seções de array (slicing) usam a sintaxe a(start:end:stride) — todas as partes opcionais. Stride pode ser negativo (reverso). Subscripts de vetor permitem gather/scatter com um array de índices: a(idx) retorna [a(idx(1)), a(idx(2)), ...]. Seções podem ser atribuídas: a(2:4) = [99,98,97]. O construto 'where' é atribuição condicional em nível de array (como numpy where). Operações de array do Fortran são vetorizadas — sem loops explícitos para operações elemento a elemento. Esse é o recurso matador do Fortran para código numérico: sintaxe limpa, matemática, que os compiladores vetorizam automaticamente.
program array_sections
implicit none
integer :: a(10) = [(i, i=1,10)]
integer :: b(5)
integer :: idx(3) = [2, 5, 7]
! Array sections (slicing): a(start:end[:stride])
print *, a(3:7) ! 3 4 5 6 7
print *, a(1:10:2) ! 1 3 5 7 9 (stride 2)
print *, a(:5) ! 1 2 3 4 5 (start defaults to 1)
print *, a(6:) ! 6 7 8 9 10 (end defaults to ubound)
print *, a(::3) ! 1 4 7 10
print *, a(10:1:-1) ! 10 9 8 ... 1 (reverse)
! Vector subscript (gather)
b = a(idx) ! b = [a(2), a(5), a(7)] = [2, 5, 7]
print *, b
! Assigning to a section
a(2:4) = [99, 98, 97]
print *, a(1:5) ! 1 99 98 97 5
! 2D sections
real :: m(4,4) = 0.0
m(2:3, 2:3) = 1.0 ! set 2x2 sub-block
print *, m(2,2), m(3,3) ! 1.0 1.0
! Where construct (masked array assignment)
where (a > 50) a = 0 ! set elements > 50 to 0
end program array_sectionsArrays Multidimensionais & Matrizes
Fortran armazena arrays em column-major (primeiro índice varia mais rápido na memória) — oposto ao C. reshape preenche em ordem column-major, então reshape([1,2,3,4],[2,2]) dá [[1,3],[2,4]]. matmul(A,B) é multiplicação verdadeira de matrizes (álgebra linear); A*B é elemento a elemento (Hadamard) — eles NÃO são a mesma coisa! transpose(A) retorna a transposta. Reduções: sum, product, maxval, minval, maxloc, minloc, count — todas suportam dim= para reduzir ao longo de um eixo. Para alto desempenho, escreva loops em ordem column-major (loop mais interno sobre o primeiro índice) para ser cache-friendly.
program matrices
implicit none
real :: A(3,3), B(3,3), C(3,3)
integer :: i, j
! Initialize with implied-do
A = reshape([(real(i), i=1,9)], [3,3])
! A = 1 4 7
! 2 5 8
! 3 6 9 (column-major fill!)
! Identity matrix
B = 0.0
do i = 1, 3
B(i,i) = 1.0
end do
! Matrix multiplication (intrinsic)
C = matmul(A, B) ! A * I = A
print *, C(1,1), C(2,2) ! 1.0 5.0
! Transpose
print *, transpose(A)(1,:) ! 1 2 3 (first row of A^T)
! Element-wise operations
C = A + B ! element-wise add
C = A * 2.0 ! scalar multiply
C = A * B ! element-wise (NOT matmul!)
! Array reduction along a dimension
print *, sum(A, dim=1) ! column sums: 6 15 24
print *, sum(A, dim=2) ! row sums: 12 15 18
print *, maxval(A) ! 9.0
print *, maxloc(A) ! 3 3 (location of max)
! Reshape
integer :: flat(6) = [1,2,3,4,5,6]
integer :: mat(2,3)
mat = reshape(flat, [2,3])
end program matricesArrays Allocatable (Dinâmicos)
Arrays allocatable são a maneira moderna de fazer memória dinâmica no Fortran — mais seguros que ponteiros (sem vazamentos de memória, desalocação automática na saída do escopo). Declare com o atributo allocatable e uma forma diferida (:, (:,:), etc.). allocate() com stat= captura erros (sempre verifique!). deallocate() libera explicitamente. Fortran 2003+ suporta realocação automática na atribuição: flex = [flex, 4] cresce o array. allocated() verifica se está atualmente alocado. Allocatables são preferidos em vez de ponteiros para arrays dinâmicos porque o compilador rastreia e libera-os automaticamente — sem vazamentos, sem ponteiros pendentes.
program alloc_demo
implicit none
integer, allocatable :: arr(:), matrix(:,:)
integer :: n, m, i, stat
! Get size from user
print *, "Enter size:"
read(*, *) n
m = n * 2
! Allocate
allocate(arr(n), matrix(n, m), stat=stat)
if (stat /= 0) then
print *, "Allocation failed!"
stop 1
end if
! Use the arrays
arr = [(i, i=1, n)]
matrix = 0.0
do i = 1, n
matrix(i, :) = i
end do
print *, size(arr), size(matrix, dim=2)
print *, allocated(arr) ! T
! Deallocate (or let it auto-deallocate at scope exit)
deallocate(arr, matrix)
print *, allocated(arr) ! F
! Automatic reallocation on assignment (Fortran 2003)
integer, allocatable :: flex(:)
flex = [1, 2, 3] ! auto-allocates to size 3
flex = [flex, 4, 5] ! reallocates to size 5: 1 2 3 4 5
print *, flex
deallocate(flex)
end program alloc_demoFunções Intrínsecas de Array
As intrínsecas de array do Fortran são seu superpoder. Reduções: sum, product, maxval, minval, maxloc (índice do máximo), minloc, count (contagem de true), any (existe), all (todos). Todas suportam mask= para redução condicional. pack() reúne elementos onde a mask é true (como numpy compress); unpack() faz scatter. cshift/eoshift rotacionam arrays (circular vs end-off). merge(a, b, mask) faz seleção elemento a elemento. Nota: Fortran NÃO tem sort built-in — você deve escrever um (ou usar uma biblioteca). Essas intrínsecas são elementais e vetorizáveis, tornando o código Fortran tanto limpo quanto rápido.
program array_funcs
implicit none
integer :: a(5) = [3, 1, 4, 1, 5, 9, 2, 6]
! Wait, let's fix size
integer :: b(8) = [3, 1, 4, 1, 5, 9, 2, 6]
integer :: c(5) = [10, 20, 30, 40, 50]
logical :: mask(5) = [.true., .false., .true., .false., .true.]
! Inquiry
print *, size(b) ! 8
print *, shape(b) ! 8
print *, lbound(b), ubound(b) ! 1 8
! Reductions
print *, sum(b) ! 31
print *, product(c) ! 120000000
print *, maxval(b), minval(b) ! 9 1
print *, maxloc(b) ! 6 (index of max)
print *, minloc(b) ! 2 (index of min, first occurrence)
print *, count(b > 3) ! 5 (number of true elements)
print *, any(b > 8) ! T (at least one)
print *, all(b > 0) ! T (all of them)
! With mask
print *, sum(b, mask=b > 3) ! sum of elements > 3
print *, pack(b, b > 3) ! compact array of elements > 3
print *, unpack([1,2], mask, 0) ! spread values per mask
! Manipulation
print *, cshift(b, 2) ! circular shift left by 2
print *, eoshift(b, 2) ! end-off shift left by 2 (fills 0)
print *, merge(b, c, mask) ! element-wise: b where mask true, else c
! Sorting (Fortran 2003+)
integer :: sorted(8)
sorted = b
call sort_array(sorted) ! custom sort (no built-in sort)
end program array_funcsStrings & Manipulação de Caracteres
Declaração de Character & Comprimento
Strings Fortran são de COMPRIMENTO FIXO por padrão — strings mais curtas são preenchidas com espaços para preencher o comprimento declarado. character(N) ou character(len=N) declara comprimento N. character(*) pega o comprimento do contexto (inicializador de parâmetro ou argumento dummy). character(:), allocatable habilita strings dinâmicas de comprimento diferido (Fortran 2003+) — a string é realocada na atribuição. len() retorna o comprimento declarado; len_trim() retorna o comprimento sem espaços à direita. trim() retorna a string sem espaços à direita (mas o resultado ainda é de comprimento fixo no contexto). Para processamento de texto de comprimento variável, use strings allocatable de comprimento diferido.
program char_decl
implicit none
! Fixed-length strings
character(10) :: s1 = "Hello"
character(len=20) :: s2 = "World"
character(20) :: s3 ! len= keyword optional
! Deferred-length (allocatable) - Fortran 2003+
character(:), allocatable :: flex
flex = "Dynamic" ! len=7
flex = "Now longer string" ! reallocates to len=18
print *, len(flex) ! 18
! Array of strings
character(15) :: names(3) = ["Alice", "Bob", "Carol"]
! Single character
character(1) :: ch = 'A'
character :: ch2 = 'B' ! len=1 default
print *, s1 ! "Hello " (padded to 10)
print *, trim(s1) ! "Hello" (no padding)
print *, len(s1), len_trim(s1) ! 10 5
print *, names(2) ! "Bob" (padded)
end program char_declConcatenação de Strings & Operações
Concatenação de strings usa o operador //. repeat(s, n) repete uma string n vezes. Substrings usam s(start:end) — 1-indexed, INCLUSIVO em ambas as extremidades (diferente do Python). s(8:) significa da posição 8 ao fim; s(:5) significa do início à posição 5. index(s, sub) retorna a posição da primeira ocorrência de sub (0 se não encontrada, sensível a maiúsculas/minúsculas). scan(s, set) retorna a posição do primeiro caractere NO conjunto; verify(s, set) retorna o primeiro caractere NÃO no conjunto. adjustl/adjustr deslocam espaços à esquerda/direita. Strings Fortran NÃO são terminadas com null como em C — o comprimento é rastreado separadamente.
program string_ops
implicit none
character(20) :: first = "John", last = "Doe"
character(50) :: full
! Concatenation with //
full = first // " " // last ! "John Doe"
print *, trim(full)
! Repeat
print *, repeat("-", 30) ! 30 dashes
! Substring (1-indexed, inclusive)
character(20) :: s = "Hello, World!"
print *, s(1:5) ! "Hello"
print *, s(8:12) ! "World"
print *, s(8:) ! "World!" (to end)
print *, s(:5) ! "Hello" (from start)
! Index (find substring)
print *, index(s, "World") ! 8 (position, 0 if not found)
print *, index(s, "world") ! 0 (case-sensitive)
print *, scan(s, "aeiou") ! 2 (first vowel position)
print *, verify(s, "abcdefg") ! 1 (first char NOT in set)
! Length
print *, len_trim(s) ! 13
print *, adjustl(s) ! left-justify
print *, adjustr(s) ! right-justify
end program string_opsFunções Intrínsecas de String
iachar(c) retorna o código ASCII de um caractere; achar(i) é o inverso. (ichar/char são dependentes do processador — prefira iachar/achar para portabilidade.) Fortran NÃO tem conversão de maiúsculas/minúsculas built-in — use iachar/achar manualmente (A-Z é 65-90, a-z é 97-122, a diferença é 32). Comparação lexicográfica: lge/lgt/lle/llt (lexicamente maior/menor que) tratam strings de comprimentos diferentes graciosamente. I/O interno (read/write para uma string em vez de um arquivo) é a maneira idiomática de converter entre strings e números: read(str, *) num e write(str, fmt) num. O formato I0 fornece inteiros de largura mínima (sem preenchimento).
program string_funcs
implicit none
character(20) :: s = "Hello World"
integer :: i
character(1) :: ch
! Character code conversion
print *, iachar('A') ! 65 (ASCII code)
print *, achar(66) ! 'B' (from ASCII code)
print *, ichar('A') ! processor-dependent (use iachar for ASCII)
! Case conversion (manual - no built-in)
do i = 1, len_trim(s)
ch = s(i:i)
if (ch >= 'A' .and. ch <= 'Z') then
s(i:i) = achar(iachar(ch) + 32) ! to lowercase
end if
end do
print *, s ! "hello world"
! Comparison
print *, lge("apple", "banana") ! F (lexicographic >=)
print *, lgt("zebra", "apple") ! T
print *, lle("abc", "abcd") ! T (<=)
print *, llt("abc", "abd") ! T (<)
! String to number conversion (internal read)
character(20) :: num_str = "3.14159"
real :: pi_val
read(num_str, *) pi_val
print *, pi_val * 2 ! 6.28318
! Number to string (internal write)
character(20) :: out_str
integer :: n = 42
write(out_str, '(I0)') n ! I0 = minimal-width integer
print *, "Number: " // trim(out_str)
end program string_funcsFormatação de Strings
Especificações de formato vivem em uma string: '(I5, F8.2, A)'. I=integer, F=ponto fixo real, E=exponencial, ES=científico (mantissa 1-10), A=character, X=espaço, /=nova linha. A largura vem primeiro (I5 = largura 5), então .m opcional para dígitos mínimos (I5.3). I0 significa largura mínima (sem preenchimento). ES fornece notação científica adequada (1.23E+6) vs E (0.12E+7). Repetições: 3I4 = três inteiros cada um com largura 4. Literais de string em formato: '("text")'. O formato pode ser uma variável string, um * (list-directed, o compilador escolhe) ou um label (número de statement). Para saída limpa, prefira I0 para inteiros e F ou ES para reais.
program format_demo
implicit none
integer :: n = 42
real :: pi = 3.14159265
real :: big = 1234567.89
character(20) :: name = "Alice"
! Format specifiers
! Iw - integer, width w
! Iw.m - integer, width w, at least m digits
! Fw.d - fixed-point real, width w, d decimals
! Ew.d - exponential, width w, d decimals
! Aw - character, width w
! A - character, default width
! nX - n spaces
! / - newline
write(*, '(I5)') n ! " 42"
write(*, '(I5.3)') n ! " 042"
write(*, '(I0)') n ! "42" (minimal)
write(*, '(F10.4)') pi ! " 3.1416"
write(*, '(E12.4)') big ! " 0.1235E+07"
write(*, '(ES12.4)') big ! " 1.2346E+06" (scientific)
write(*, '(A10)') name ! " Alice"
write(*, '(A, I3)') "n=", n ! "n= 42"
! Multiple items
write(*, '(A, I3, A, F8.4)') "n=", n, " pi=", pi
! Repeated format: 3I4 = three integers width 4
write(*, '(3I4)') 1, 2, 3 ! " 1 2 3"
! Newline and spacing
write(*, '("Name: ", A, /, "Age: ", I3)') name, n
! List-directed (default format)
print *, name, n, pi
end program format_demoParsing & Tokenização
Fortran NÃO tem split/tokenize built-in — você deve escrevê-lo manualmente usando index() e substrings. O padrão: encontre o delimitador com index, extraia o token com substring, avance além do delimitador, repita. Para pares key=value, encontre '=' com index e divida em key (antes) e value (depois). trim() remove espaços à direita; adjustl() remove espaços à esquerda. Para parsing robusto, também trate tokens vazios e whitespace. Alternativamente, use reads internos com especificadores de formato para dados estruturados, ou leia de uma string como se fosse um arquivo. Bibliotecas como split() existem em alguns frameworks Fortran, mas não são padrão.
program parse_demo
implicit none
character(100) :: line = "name=Bob,age=30,city=NYC"
character(50) :: token
integer :: pos, start, end
! Split by comma
start = 1
do
end = index(line(start:), ",")
if (end == 0) then
! last token
token = line(start:)
call process(trim(token))
exit
end if
token = line(start:start+end-2)
call process(trim(token))
start = start + end
end do
contains
subroutine process(t)
character(*), intent(in) :: t
integer :: eq_pos
eq_pos = index(t, "=")
if (eq_pos > 0) then
print *, "Key: ", trim(t(:eq_pos-1)), &
" Value: ", trim(t(eq_pos+1:))
end if
end subroutine process
end program parse_demo
! Output:
! Key: name Value: Bob
! Key: age Value: 30
! Key: city Value: NYCProcedimentos: Funções & Subroutines
Funções
Funções retornam um valor e são usadas em expressões (como funções matemáticas). Sintaxe moderna: 'function name(args) result(var)' — a variável de resultado é o que é retornado. intent(in) marca argumentos somente leitura (o compilador impõe isso). Funções podem retornar escalares OU arrays (use size() da entrada para dimensionar a saída). Funções devem ser PURAS (sem efeitos colaterais) — não modifique estado global nem faça I/O em uma função. Procedimentos internos (no bloco 'contains') têm acesso às variáveis do host (host association). Para procedimentos externos, use um bloco interface para especificar a assinatura.
program func_demo
implicit none
! Function declared in interface or as external
print *, add(3, 4) ! 7
print *, square(5) ! 25
print *, distance(0.0, 0.0, 3.0, 4.0) ! 5.0
contains
! Basic function with result clause
function add(a, b) result(c)
integer, intent(in) :: a, b
integer :: c
c = a + b
end function add
! Function with result named same as function (legacy)
function square(x) result(y)
integer, intent(in) :: x
integer :: y
y = x * x
end function square
! Real function
function distance(x1, y1, x2, y2) result(d)
real, intent(in) :: x1, y1, x2, y2
real :: d
d = sqrt((x2-x1)**2 + (y2-y1)**2)
end function distance
! Array-valued function
function reverse(arr) result(rev)
integer, intent(in) :: arr(:)
integer :: rev(size(arr))
integer :: i, n
n = size(arr)
do i = 1, n
rev(i) = arr(n-i+1)
end do
end function reverse
end program func_demoSubroutines & Intent
Subroutines são chamadas com 'call' e não retornam um valor — elas modificam argumentos in place. Use subroutines quando: (1) você precisa modificar múltiplos argumentos, (2) a operação é um 'comando' não uma 'computação', (3) retornar um resultado array-valued é awkward. Atributos intent documentam e impõem a direção do argumento: intent(in) = somente leitura (erro de compilação se você atribuir a ele), intent(out) = somente escrita (indefinido na entrada, deve ser definido antes do retorno), intent(inout) = leitura-escrita. Sempre especifique intent — captura bugs e habilita otimização. Subroutines podem modificar arrays passados a elas (sem cópia se forem contíguos).
program sub_demo
implicit none
integer :: x = 10, y = 20
real :: arr(5) = [1.0, 2.0, 3.0, 4.0, 5.0]
! Subroutines are called with 'call'
call swap(x, y)
print *, x, y ! 20 10
call scale_array(arr, 2.0)
print *, arr ! 2 4 6 8 10
call fill_zero(arr)
print *, arr ! 0 0 0 0 0
contains
subroutine swap(a, b)
integer, intent(inout) :: a, b ! read AND write
integer :: tmp
tmp = a
a = b
b = tmp
end subroutine swap
subroutine scale_array(a, factor)
real, intent(inout) :: a(:)
real, intent(in) :: factor
a = a * factor ! whole-array operation
end subroutine scale_array
subroutine fill_zero(a)
real, intent(out) :: a(:) ! write-only (output)
a = 0.0
end subroutine fill_zero
end program sub_demoFunções Pure & Elemental
Funções pure NÃO têm efeitos colaterais: sem I/O, sem modificação de variável global, sem stop, só podem chamar outros procedimentos pure. Elas habilitam otimização do compilador (paralelização, eliminação de subexpressões comuns) e são exigidas em alguns contextos (ex.: DO CONCURRENT). Funções elemental são escritas para ESCALARES mas automaticamente funcionam em arrays elemento a elemento — escreva uma vez, use para ambos. pure elemental combina ambos. Use pure para qualquer função que seja realmente uma função matemática (sem efeitos colaterais). Use elemental quando a operação se aplica naturalmente elemento a elemento a arrays (funções matemáticas, conversões). O compilador pode vetorizar automaticamente chamadas elemental em arrays.
program pure_demo
implicit none
real :: a(5) = [1.0, 2.0, 3.0, 4.0, 5.0]
real :: b(5)
integer :: i
! Pure function: no side effects, no I/O
b = square_arr(a)
print *, b ! 1 4 9 16 25
! Elemental function: works on scalars AND arrays automatically
b = cube(a) ! applies cube() element-wise
print *, b ! 1 8 27 64 125
print *, cube(2.0) ! also works on scalar: 8.0
contains
! Pure: no side effects, no I/O, no stop, only pure calls
pure function square_arr(x) result(y)
real, intent(in) :: x(:)
real :: y(size(x))
y = x * x
end function square_arr
! Elemental: scalar signature, but works on arrays too
elemental function cube(x) result(y)
real, intent(in) :: x
real :: y
y = x * x * x
end function cube
! Pure elemental: both
pure elemental double precision function sq(x) result(y)
double precision, intent(in) :: x
double precision :: y
y = x * x
end function sq
end program pure_demoArgumentos Opcionais & por Palavra-chave
Argumentos opcionais permitem que chamadores os omitam. Use present(arg) dentro do procedimento para verificar se um argumento foi fornecido — acessar um optional ausente é comportamento indefinido. Argumentos por palavra-chave (name="value") permitem passar argumentos em qualquer ordem e tornam as chamadas autodocumentadas. Depois de usar uma palavra-chave, todos os argumentos subsequentes também devem usar palavras-chave. Argumentos opcionais devem vir após todos os obrigatórios na assinatura. Valores padrão são implementados via verificações present() (Fortran não tem sintaxe padrão built-in). Palavra-chave + opcional juntos habilitam APIs flexíveis: chamadores especificam apenas o que precisam.
program optional_demo
implicit none
! All arguments after the first optional must also be optional
print *, greet("Alice") ! Hello, Alice!
print *, greet("Bob", "Hi") ! Hi, Bob!
print *, greet("Carol", greeting="Hey") ! keyword argument
print *, greet(greeting="Welcome", name="Dave") ! all keywords
! With present() check
call log_msg("Starting up")
call log_msg("Error!", level=2)
call log_msg("Debug info", level=0, file="debug.log")
contains
function greet(name, greeting) result(msg)
character(*), intent(in) :: name
character(*), intent(in), optional :: greeting
character(50) :: msg
character(20) :: g
if (present(greeting)) then
g = greeting
else
g = "Hello"
end if
msg = trim(g) // ", " // name // "!"
end function greet
subroutine log_msg(message, level, file)
character(*), intent(in) :: message
integer, intent(in), optional :: level
character(*), intent(in), optional :: file
integer :: lvl
lvl = 1
if (present(level)) lvl = level
print *, "[L", lvl, "] ", trim(message)
end subroutine log_msg
end program optional_demoProcedimentos Internos & Recursivos
Procedimentos internos (dentro de 'contains') têm host association — eles podem ler E modificar as variáveis do programa host (como closures). Use-os para helpers que precisam do estado do host. Procedimentos recursivos devem ser declarados com o prefixo 'recursive' (Fortran 90/2003); Fortran 2018 torna recursão o padrão. Para recursão mútua, use um bloco interface para declarar a referência forward. Recursão é elegante, mas pode ser lenta (overhead de chamada de função) e arriscada (stack overflow para recursão profunda). Para factorial/fibonacci, versões iterativas são mais rápidas e seguras. Use recursão para problemas naturalmente recursivos (traversal de árvore, divide-and-conquer) com profundidade limitada.
program nested_demo
implicit none
integer :: counter = 0 ! host variable
! Internal procedures (in contains) access host variables
call increment
call increment
print *, counter ! 2
! Recursive function
print *, factorial(5) ! 120
print *, fib(10) ! 55
! Mutual recursion (needs forward declaration)
print *, is_even(4) ! T
contains
subroutine increment
counter = counter + 1 ! modifies host's counter
end subroutine increment
recursive function factorial(n) result(f)
integer, intent(in) :: n
integer :: f
if (n <= 1) then
f = 1
else
f = n * factorial(n-1)
end if
end function factorial
recursive function fib(n) result(f)
integer, intent(in) :: n
integer :: f
if (n < 2) then
f = n
else
f = fib(n-1) + fib(n-2)
end if
end function fib
! Mutual recursion with interface
recursive function is_even(n) result(r)
integer, intent(in) :: n
logical :: r
interface
recursive function is_odd(m) result(ro)
integer, intent(in) :: m
logical :: ro
end function is_odd
end interface
if (n == 0) then
r = .true.
else
r = is_odd(n-1)
end if
end function is_even
end program nested_demoMódulos & Encapsulação
Básico de Módulos & Use
Módulos são o principal mecanismo de encapsulação do Fortran (substituindo common blocks e procedimentos externos). Um arquivo de módulo contém: (1) declarações (constantes, variáveis, tipos derivados), (2) um bloco 'contains' com procedimentos. Use 'use module_name' para importar; 'use module_name, only: x, y' importa apenas entidades específicas (recomendado — evita poluição de namespace). Variáveis de módulo são persistentes (static) e compartilhadas entre todos os procedimentos que usam o módulo. Módulos fornecem interfaces explícitas (o compilador verifica tipos de argumentos), diferente de procedimentos externos. Sempre compile arquivos de módulo antes dos arquivos que os usam. 'implicit none' em um módulo propaga para todos os seus procedimentos.
! geometry.f90 - module file
module geometry
implicit none
private ! default: everything private
public :: circle_area, circle_perimeter, PI
! Module-level constants (persistent)
real, parameter :: PI = 3.14159265
contains
function circle_area(r) result(a)
real, intent(in) :: r
real :: a
a = PI * r * r
end function circle_area
function circle_perimeter(r) result(p)
real, intent(in) :: r
real :: p
p = 2.0 * PI * r
end function circle_perimeter
end module geometry
! main.f90 - using the module
program use_module
use geometry, only: circle_area, circle_perimeter, PI
implicit none
real :: r = 5.0
print *, "Area: ", circle_area(r) ! 78.5398
print *, "Perimeter: ", circle_perimeter(r) ! 31.4159
print *, "PI: ", PI
end program use_module
! Compile: gfortran geometry.f90 main.f90 -o mainControle de Acesso (Public/Private)
Controle de acesso: 'private' torna entidades internas ao módulo; 'public' as exporta. O padrão pode ser definido em nível de módulo ('private' então seletivamente 'public :: ...') — esta é a melhor prática (interface explícita). Componentes de tipos derivados podem ser private mesmo se o tipo em si for public — chamadores podem usar o tipo, mas não acessar internals diretamente; eles devem passar por procedimentos. 'save' torna variáveis de módulo persistentes (elas retêm valores entre chamadas) — variáveis de módulo são salvas por padrão. 'final' define um destrutor (chamado quando o objeto sai do escopo). Essa encapsulação habilita verdadeiro OOP com invariantes impostos através de procedimentos.
module bank_account
implicit none
private ! default: everything private
! Explicitly export:
public :: account_t, deposit, withdraw, get_balance
! Derived type - can expose type but hide internals
type :: account_t
private ! components are private
real :: balance = 0.0
integer :: id = 0
contains
procedure :: balance => get_bal ! type-bound procedure
final :: cleanup ! destructor
end type account_t
! Module-level counter (private, not exported)
integer, save :: next_id = 1000
contains
! Constructor (factory function)
function create_account(initial) result(acc)
type(account_t) :: acc
real, intent(in) :: initial
acc%balance = initial
acc%id = next_id
next_id = next_id + 1
end function create_account
subroutine deposit(acc, amount)
type(account_t), intent(inout) :: acc
real, intent(in) :: amount
acc%balance = acc%balance + amount
end subroutine deposit
function get_bal(acc) result(b)
class(account_t), intent(in) :: acc
real :: b
b = acc%balance
end function get_bal
subroutine cleanup(acc)
type(account_t) :: acc
! cleanup code (e.g., log closure)
end subroutine cleanup
end module bank_accountTipos Derivados em Módulos
Tipos derivados definidos em módulos podem ter: procedimentos type-bound (procedure :: name => impl), construtores (via interface overloaded com o nome do tipo) e componentes allocatable. O 'class(keyword)' em procedimentos type-bound habilita polimorfismo (o tipo real pode ser uma subclasse). Procedimentos type-bound são chamados como obj%method(args) — sintaxe OOP. Sobrecarregar o nome do tipo como um interface permite ter múltiplos construtores (vector_from_array, vector_from_size). Componentes allocatable são automaticamente alocados/desalocados. Este é OOP moderno em Fortran: encapsulação, métodos, construtores e polimorfismo, todos dentro do sistema de módulos.
module vector_mod
implicit none
private
public :: vector_t, vector_add, vector_scale
! Derived type with type parameters (Fortran 2003)
type :: vector_t
real, allocatable :: data(:)
integer :: length = 0
contains
procedure :: norm => vector_norm
procedure :: print => vector_print
end type vector_t
! Constructor interface (overloaded)
interface vector_t
procedure vector_from_array
procedure vector_from_size
end interface vector_t
contains
function vector_from_array(arr) result(v)
real, intent(in) :: arr(:)
type(vector_t) :: v
v%data = arr
v%length = size(arr)
end function
function vector_from_size(n, fill) result(v)
integer, intent(in) :: n
real, intent(in) :: fill
type(vector_t) :: v
allocate(v%data(n))
v%data = fill
v%length = n
end function
function vector_norm(self) result(n)
class(vector_t), intent(in) :: self
real :: n
n = sqrt(sum(self%data**2))
end function
subroutine vector_print(self)
class(vector_t), intent(in) :: self
print *, "Vector(len=", self%length, "): ", self%data
end subroutine
function vector_add(a, b) result(c)
type(vector_t), intent(in) :: a, b
type(vector_t) :: c
c%data = a%data + b%data
c%length = a%length
end function
function vector_scale(a, s) result(c)
type(vector_t), intent(in) :: a
real, intent(in) :: s
type(vector_t) :: c
c%data = a%data * s
c%length = a%length
end function
end module vector_modProcedimentos Genéricos & Sobrecarga
Interfaces genéricas fornecem polimorfismo ad-hoc (sobrecarga): um nome despacha para diferentes procedimentos específicos com base nos tipos de argumentos. O bloco 'interface name / module procedure proc1, proc2 / end interface' lista todos os procedimentos específicos. O compilador escolhe o correspondente por tipo/rank do argumento em tempo de compilação. Todos os procedimentos específicos em um genérico devem ter assinaturas DISTINTAS (distinguíveis pelos tipos de argumentos) — caso contrário, ambiguidade. É assim que o Fortran faz sobrecarga de operadores/funções sem templates. O dispatch genérico é resolvido em tempo de compilação (sem overhead em runtime). Use genéricos para fornecer uma API uniforme entre tipos.
module generics_mod
implicit none
private
public :: print_value, add
! Generic interface: one name, multiple specific procedures
interface print_value
module procedure print_int
module procedure print_real
module procedure print_str
module procedure print_int_array
end interface print_value
interface add
module procedure add_int
module procedure add_real
module procedure add_arrays
end interface add
contains
subroutine print_int(x)
integer, intent(in) :: x
print *, "Integer: ", x
end subroutine
subroutine print_real(x)
real, intent(in) :: x
print *, "Real: ", x
end subroutine
subroutine print_str(s)
character(*), intent(in) :: s
print *, "String: ", trim(s)
end subroutine
subroutine print_int_array(arr)
integer, intent(in) :: arr(:)
print *, "Array: ", arr
end subroutine
function add_int(a, b) result(c)
integer, intent(in) :: a, b
integer :: c
c = a + b
end function
function add_real(a, b) result(c)
real, intent(in) :: a, b
real :: c
c = a + b
end function
function add_arrays(a, b) result(c)
real, intent(in) :: a(:), b(:)
real :: c(size(a))
c = a + b
end function
end module generics_mod
program use_generics
use generics_mod
implicit none
call print_value(42) ! Integer: 42
call print_value(3.14) ! Real: 3.14
call print_value("Hello") ! String: Hello
call print_value([1,2,3]) ! Array: 1 2 3
print *, add(2, 3) ! 5
print *, add(2.5, 3.5) ! 6.0
end program use_genericsSobrecarga de Operadores
Sobrecarga de operadores permite definir como +, -, *, /, ==, etc. funcionam em seus tipos derivados. interface operator(+) / module procedure vec_add / end interface vincula o operador + a uma função. Para operadores binários, você pode sobrecarregar ambas as ordens (vec*scalar e scalar*vec) com procedimentos separados. assignment(=) sobrecarrega o operador de atribuição (o procedimento é uma subroutine com LHS intent(out) e RHS intent(in)). Isso habilita sintaxe matemática: c = a + b em vez de c = vec_add(a, b). Use sobrecarga de operadores para tipos matemáticos (vetores, matrizes, números complexos) onde melhora a legibilidade. Evite sobrecarregar para semânticas não óbvias. O construtor de estrutura vec3(x,y,z) é built-in para tipos derivados.
module vec_ops
implicit none
private
public :: vec3, operator(+), operator(*), assignment(=)
type :: vec3
real :: x, y, z
end type vec3
! Overload the + operator for vec3 + vec3
interface operator(+)
module procedure vec_add
end interface
! Overload * for vec3 * scalar and scalar * vec3
interface operator(*)
module procedure vec_scale_r
module procedure vec_scale_l
module procedure vec_dot
end interface
! Overload = for array-to-vec assignment
interface assignment(=)
module procedure arr_to_vec
end interface
contains
function vec_add(a, b) result(c)
type(vec3), intent(in) :: a, b
type(vec3) :: c
c = vec3(a%x+b%x, a%y+b%y, a%z+b%z)
end function
function vec_scale_r(v, s) result(r)
type(vec3), intent(in) :: v
real, intent(in) :: s
type(vec3) :: r
r = vec3(v%x*s, v%y*s, v%z*s)
end function
function vec_scale_l(s, v) result(r)
real, intent(in) :: s
type(vec3), intent(in) :: v
type(vec3) :: r
r = vec3(v%x*s, v%y*s, v%z*s)
end function
! vec3 * vec3 = dot product (scalar)
function vec_dot(a, b) result(d)
type(vec3), intent(in) :: a, b
real :: d
d = a%x*b%x + a%y*b%y + a%z*b%z
end function
subroutine arr_to_vec(v, arr)
type(vec3), intent(out) :: v
real, intent(in) :: arr(3)
v = vec3(arr(1), arr(2), arr(3))
end subroutine
end module vec_ops
program use_ops
use vec_ops
implicit none
type(vec3) :: a, b, c
a = vec3(1.0, 2.0, 3.0)
b = vec3(4.0, 5.0, 6.0)
c = a + b ! vec3 addition
print *, c%x, c%y, c%z ! 5 7 9
print *, a * 2.0 ! scale: 2 4 6
print *, 3.0 * b ! scale: 12 15 18
print *, a * b ! dot product: 32
end program use_opsTipos Derivados (Structs) & OOP
Definindo Tipos Derivados
Tipos derivados são os structs do Fortran (tipos compostos definidos pelo usuário). Defina com 'type :: Name ... end type Name'. Componentes acessados com % (NÃO . — isso é para números complexos). Construtor de estrutura: Name(val1, val2) cria uma instância. Componentes podem ter valores padrão (= value na declaração). Atribuição de todo o tipo copia todos os componentes (cópia profunda para componentes allocatable). Arrays de tipos derivados são suportados. Tipos derivados são a fundação para OOP no Fortran (com procedimentos type-bound, herança, polimorfismo). Use % para acesso a componentes: obj%field, obj%method(). A sintaxe de construtor Name(args) é automática, a menos que você a sobrescreva com um interface.
program derived_types
implicit none
! Basic derived type (struct)
type :: Point
real :: x, y
end type Point
! Type with default initialization
type :: Person
character(20) :: name = "Unknown"
integer :: age = 0
logical :: active = .true.
end type Person
! Declare and construct
type(Point) :: p1, p2
type(Person) :: alice, bob
! Structure constructor
p1 = Point(3.0, 4.0)
p2 = Point(0.0, 0.0)
alice = Person("Alice", 30, .true.)
bob = Person("Bob", 25) ! uses default for 'active'
! Access components with %
print *, p1%x, p1%y ! 3.0 4.0
print *, alice%name, alice%age ! Alice 30
print *, bob%active ! T (default)
! Modify components
p1%x = p1%x + 1.0
alice%age = 31
! Whole-type assignment (component-wise copy)
p2 = p1
print *, p2%x ! 4.0
! Array of derived types
type(Point) :: points(3)
points(1) = Point(1.0, 1.0)
points(2) = Point(2.0, 2.0)
points(3) = Point(3.0, 3.0)
print *, points(2)%y ! 2.0
end program derived_typesComponentes de Tipo & Construtores
Tipos derivados podem ter: componentes allocatable (memória gerenciada automaticamente), componentes com inicialização padrão, procedimentos type-bound (métodos) e finalizers (destrutores). O 'interface TypeName / module procedure custom_init / end interface' sobrecarrega o construtor de estrutura com uma função factory personalizada. 'class(ClassName)' (vs 'type(ClassName)') em procedimentos type-bound habilita polimorfismo (o tipo real pode ser uma subclasse). Procedimentos 'final' rodam quando um objeto sai do escopo (destrutor) — use-os para liberar recursos. Componentes allocatable são automaticamente desalocados na finalização, mas finalizers explícitos são mais claros para limpeza complexa. Construtos block permitem declarar variáveis no meio do código (Fortran 2008).
program type_components
implicit none
! Type with various component kinds
type :: Student
integer :: id
character(20) :: name
real, allocatable :: grades(:) ! allocatable component
integer :: num_grades = 0
contains
procedure :: add_grade
procedure :: average => student_avg
final :: student_finalize
end type Student
! Overloaded constructor
interface Student
module procedure student_init
end interface Student
type(Student) :: s
! Use custom constructor
s = Student(101, "Alice")
call s%add_grade(85.0)
call s%add_grade(92.0)
call s%add_grade(78.0)
print *, s%average() ! 85.0
contains
function student_init(id, name) result(s)
integer, intent(in) :: id
character(*), intent(in) :: name
type(Student) :: s
s%id = id
s%name = name
s%num_grades = 0
end function
subroutine add_grade(self, g)
class(Student), intent(inout) :: self
real, intent(in) :: g
integer :: n
n = self%num_grades
if (n == 0) then
allocate(self%grades(1))
else
! grow array
block
real, allocatable :: tmp(:)
tmp = self%grades
deallocate(self%grades)
allocate(self%grades(n+1))
self%grades(1:n) = tmp
end block
end if
self%num_grades = n + 1
self%grades(n+1) = g
end subroutine
function student_avg(self) result(avg)
class(Student), intent(in) :: self
real :: avg
if (self%num_grades > 0) then
avg = sum(self%grades) / self%num_grades
else
avg = 0.0
end if
end function
subroutine student_finalize(self)
type(Student) :: self
if (allocated(self%grades)) deallocate(self%grades)
end subroutine
end program type_componentsProcedimentos Type-Bound (Métodos)
Procedimentos type-bound são os métodos do Fortran: 'procedure :: method_name => implementation'. Chame-os como obj%method(args) — sintaxe OOP. O primeiro argumento é 'self' (o objeto), declarado como 'class(TypeName)' (polimórfico) ou 'type(TypeName)' (concreto). 'class' permite herança/polimorfismo; 'type' é para tipos não extensíveis. O '=> implementation' mapeia o nome do método a um procedimento específico (permite renomeação). final é o destrutor. Este exemplo implementa uma stack dinâmica com array de crescimento automático. Procedimentos type-bound dão verdadeiro OOP: encapsulação (dados + métodos juntos), sintaxe de passagem de mensagens (obj%method) e polimorfismo (via class). Sempre use class() para procedimentos type-bound para habilitar herança futura.
module stack_mod
implicit none
private
public :: stack_t
type :: stack_t
integer, allocatable :: data(:)
integer :: top = 0
integer :: capacity = 0
contains
procedure :: push => stack_push
procedure :: pop => stack_pop
procedure :: peek => stack_peek
procedure :: is_empty => stack_is_empty
procedure :: size => stack_size
procedure :: clear => stack_clear
final :: stack_finalize
end type stack_t
interface stack_t
module procedure stack_init
end interface
contains
function stack_init(initial_cap) result(s)
integer, intent(in), optional :: initial_cap
type(stack_t) :: s
integer :: cap
cap = 16
if (present(initial_cap)) cap = initial_cap
allocate(s%data(cap))
s%capacity = cap
s%top = 0
end function
subroutine stack_push(self, val)
class(stack_t), intent(inout) :: self
integer, intent(in) :: val
if (self%top >= self%capacity) then
! grow
block
integer, allocatable :: tmp(:)
tmp = self%data
deallocate(self%data)
allocate(self%data(self%capacity * 2))
self%data(1:self%capacity) = tmp
self%capacity = self%capacity * 2
end block
end if
self%top = self%top + 1
self%data(self%top) = val
end subroutine
function stack_pop(self) result(val)
class(stack_t), intent(inout) :: self
integer :: val
if (self%top == 0) stop "Stack underflow"
val = self%data(self%top)
self%top = self%top - 1
end function
function stack_peek(self) result(val)
class(stack_t), intent(in) :: self
integer :: val
val = self%data(self%top)
end function
logical function stack_is_empty(self) result(r)
class(stack_t), intent(in) :: self
r = (self%top == 0)
end function
integer function stack_size(self) result(n)
class(stack_t), intent(in) :: self
n = self%top
end function
subroutine stack_clear(self)
class(stack_t), intent(inout) :: self
self%top = 0
end subroutine
subroutine stack_finalize(self)
type(stack_t) :: self
if (allocated(self%data)) deallocate(self%data)
end subroutine
end module stack_mod
program use_stack
use stack_mod
implicit none
type(stack_t) :: s
s = stack_t(8) ! initial capacity 8
call s%push(10)
call s%push(20)
call s%push(30)
print *, s%size() ! 3
print *, s%pop() ! 30
print *, s%pop() ! 20
print *, s%is_empty() ! F
end program use_stackHerança & Polimorfismo
OOP do Fortran: 'type, extends(Parent) :: Child' cria uma subclasse (herança). 'type, abstract :: Name' com 'procedure(...), deferred :: method' define uma base abstrata (como classe abstrata Java / C++ pure virtual). 'class(Base)' é polimórfico — pode segurar qualquer subclasse. Dispatch polimórfico: chamar obj%method() invoca a sobrescrita da subclasse. 'select type (var => expr) / type is (ConcreteType) / end select' faz verificação de tipo em runtime (downcasting). Aloque com 'allocate(TypeName::var)' para criar um objeto polimórfico de um tipo concreto específico. Isso é OOP completo: herança, polimorfismo, tipos abstratos e dispatch em runtime — comparável a Java/C++.
module shapes
implicit none
private
public :: shape, circle, rectangle, shape_ptr
! Base type (abstract)
type, abstract :: shape
character(20) :: name
contains
procedure(area_if), deferred :: area
procedure(describe_if), deferred :: describe
procedure :: get_name => shape_get_name
end type shape
! Abstract interface (must be implemented by subclasses)
abstract interface
function area_if(self) result(a)
import :: shape
class(shape), intent(in) :: self
real :: a
end function area_if
subroutine describe_if(self)
import :: shape
class(shape), intent(in) :: self
end subroutine describe_if
end interface
! Derived type: circle extends shape
type, extends(shape) :: circle
real :: radius
contains
procedure :: area => circle_area
procedure :: describe => circle_describe
end type circle
! Derived type: rectangle extends shape
type, extends(shape) :: rectangle
real :: width, height
contains
procedure :: area => rect_area
procedure :: describe => rect_describe
end type rectangle
! Class pointer for polymorphism
type :: shape_ptr
class(shape), allocatable :: ptr
end type shape_ptr
contains
function shape_get_name(self) result(n)
class(shape), intent(in) :: self
character(20) :: n
n = self%name
end function
function circle_area(self) result(a)
class(circle), intent(in) :: self
real :: a
a = 3.14159265 * self%radius**2
end function
subroutine circle_describe(self)
class(circle), intent(in) :: self
print *, "Circle '", trim(self%name), "' r=", self%radius
end subroutine
function rect_area(self) result(a)
class(rectangle), intent(in) :: self
real :: a
a = self%width * self%height
end function
function rect_describe(self) result(s)
class(rectangle), intent(in) :: self
character(50) :: s
write(s, '("Rectangle ", F6.2, "x", F6.2)') self%width, self%height
end function
end module shapes
program use_polymorphism
use shapes
implicit none
type(shape_ptr) :: shapes_arr(2)
! Allocate concrete types into polymorphic container
allocate(circle::shapes_arr(1)%ptr)
select type(s => shapes_arr(1)%ptr)
type is (circle)
s%radius = 5.0
s%name = "C1"
end select
allocate(rectangle::shapes_arr(2)%ptr)
select type(s => shapes_arr(2)%ptr)
type is (rectangle)
s%width = 3.0
s%height = 4.0
s%name = "R1"
end select
! Polymorphic dispatch
block
integer :: i
do i = 1, 2
call shapes_arr(i)%ptr%describe
print *, "Area: ", shapes_arr(i)%ptr%area()
end do
end block
end program use_polymorphismTipos Aninhados & Arrays de Tipos
Tipos derivados podem ser aninhados (composição): um tipo pode ter componentes de outros tipos derivados. Acesse componentes aninhados com % encadeado: emp%home%city. Construtores de estrutura aninham naturalmente: Employee(id, name, Address(...), salary). Arrays de tipos derivados são suportados: type(Employee) :: emps(N). Você pode extrair um array de um único componente: emps(:)%id dá um array de inteiros. Componentes allocatable permitem coleções de tamanho dinâmico (ex.: um departamento com um número variável de funcionários). Esse modelo de composição é a fundação para construir estruturas de dados complexas (árvores, grafos, listas) no Fortran. Use composição (tem-um) em vez de herança (é-um) quando não há uma relação clara de subtipo.
program nested_types
implicit none
! Nested derived types
type :: Address
character(50) :: street
character(30) :: city
character(10) :: zip
end type Address
type :: Employee
integer :: id
character(30) :: name
type(Address) :: home ! nested type
real :: salary
end type Employee
type :: Department
character(30) :: name
type(Employee), allocatable :: employees(:) ! array of types
integer :: count = 0
end type Department
! Construct with nested structure constructor
type(Employee) :: emp
emp = Employee(101, "Alice", &
Address("123 Main St", "Springfield", "12345"), 75000.0)
! Access nested components
print *, emp%name ! Alice
print *, emp%home%city ! Springfield
print *, emp%home%zip ! 12345
! Department with array of employees
type(Department) :: dept
dept%name = "Engineering"
allocate(dept%employees(3))
dept%employees(1) = emp
dept%employees(2) = Employee(102, "Bob", &
Address("456 Oak Ave", "Shelbyville", "54321"), 68000.0)
dept%count = 2
! Iterate over array of types
block
integer :: i
do i = 1, dept%count
print *, dept%employees(i)%id, trim(dept%employees(i)%name), &
dept%employees(i)%home%city
end do
end block
! Array of derived type components
print *, dept%employees(1:2)%id ! array of id field
end program nested_typesI/O de Arquivos & Formatação
Abrindo & Fechando Arquivos
open() conecta um arquivo a um número de unit. newunit=u deixa o compilador escolher uma unit única (evita conflitos) — sempre prefira isso em vez de números de unit fixos no código. status: 'old' (o arquivo deve existir), 'new' (não deve existir), 'replace' (excluir + criar), 'scratch' (temporário, auto-excluído ao fechar). action: 'read', 'write', 'readwrite'. position: 'rewind' (início), 'append' (fim), 'asis' (onde estiver). Sempre verifique iostat após open e read — não-zero significa erro (negativo = EOF, positivo = erro). iomsg dá uma mensagem de erro descritiva. close() desconecta. Para tratamento robusto de arquivos, sempre verifique iostat e trate erros graciosamente.
program file_open
implicit none
integer :: u, ios
character(100) :: msg
! newunit: compiler picks a unique unit number (Fortran 2008)
! status: 'old' (must exist), 'new' (must not exist),
! 'replace' (overwrite), 'scratch' (temporary)
! action: 'read', 'write', 'readwrite'
! position: 'rewind', 'append', 'asis'
open(newunit=u, file="data.txt", status="replace", &
action="write", iostat=ios, iomsg=msg)
if (ios /= 0) then
print *, "Open failed: ", trim(msg)
stop 1
end if
write(u, *) "First line"
write(u, *) "Second line"
close(u)
! Append to existing file
open(newunit=u, file="data.txt", status="old", &
action="write", position="append", iostat=ios)
if (ios == 0) then
write(u, *) "Appended line"
close(u)
end if
! Scratch file (auto-deleted on close)
open(newunit=u, status="scratch", action="readwrite")
write(u, *) "Temporary data"
rewind(u)
! read back...
close(u) ! file disappears
! Reading with end-of-file detection
open(newunit=u, file="data.txt", status="old", action="read")
do
read(u, '(A)', iostat=ios) msg
if (ios /= 0) exit ! EOF or error
print *, trim(msg)
end do
close(u)
end program file_openI/O Formatado
Especificadores de formato controlam I/O: '(A, I0, F8.2)'. A=character, I0=integer largura mínima, F8.2=real largura 8 com 2 decimais. write(unit, fmt) escreve; read(unit, fmt) lê. unit=* significa stdout/stdin. Para arquivos, use a unit de open(). I/O list-directed (*) é flexível: read(u, *) a, b, c lê valores separados por vírgula/espaço automaticamente. Para CSV, list-directed read funciona se os valores forem separados por vírgula. Strings de formato podem ser reusadas: '(3I4)' aplica I4 três vezes. Sempre corresponda o formato ao tipo de dado — formatos incompatíveis causam erros de runtime. Para texto misto + números, leia como string e depois faça parse, ou use formatos explícitos.
program formatted_io
implicit none
integer :: u, n = 42
real :: pi = 3.14159265
character(20) :: name = "Alice"
! Write formatted data to file
open(newunit=u, file="output.txt", status="replace")
write(u, '(A, I0)') "Count: ", n
write(u, '(A, F8.4)') "Pi: ", pi
write(u, '(A, A)') "Name: ", trim(name)
write(u, '(3(I0, 1X))') 1, 2, 3 ! "1 2 3 "
close(u)
! Read formatted data back
open(newunit=u, file="output.txt", status="old", action="read")
block
character(100) :: line
integer :: i
do i = 1, 5
read(u, '(A)') line
print *, trim(line)
end do
end block
close(u)
! Reading structured data
open(newunit=u, file="data.csv", status="replace")
write(u, '(I0, ",", I0, ",", I0)') 1, 10, 100
write(u, '(I0, ",", I0, ",", I0)') 2, 20, 200
close(u)
! Read back as numbers
open(newunit=u, file="data.csv", status="old", action="read")
block
integer :: a, b, c
! List-directed read handles commas as separators
read(u, *) a, b, c
print *, a, b, c ! 1 10 100
read(u, *) a, b, c
print *, a, b, c ! 2 20 200
end block
close(u)
end program formatted_ioI/O Não-formatado (Binário)
I/O não-formatado (binário) é mais rápido e compacto que I/O formatado (texto) — sem conversão de string. form='unformatted' o habilita. access='stream' (Fortran 2003) dá acesso byte-stream (como I/O de arquivo C, sem record markers). access='sequential' (padrão) usa record markers (cada write/read é um record com prefixos de comprimento) — portável dentro do Fortran, mas não para outras linguagens. Para interoperabilidade com C/Python, use stream access. Arquivos binários não são legíveis por humanos, mas ideais para grandes conjuntos de dados numéricos. Sempre escreva metadados (tamanhos de array, informações de tipo) antes dos dados para que você possa lê-los de volta corretamente. I/O não-formatado preserva precisão total (sem arredondamento da conversão de texto).
program binary_io
implicit none
integer :: u, i
real :: arr(5) = [1.0, 2.0, 3.0, 4.0, 5.0]
real :: arr_in(5)
integer :: n
! Write binary (unformatted) - faster, no format conversion
open(newunit=u, file="data.bin", status="replace", &
form="unformatted", access="stream")
write(u) size(arr) ! write the count first
write(u) arr ! write the whole array
close(u)
! Read binary back
open(newunit=u, file="data.bin", status="old", &
form="unformatted", access="stream", action="read")
read(u) n ! read the count
print *, "Count: ", n
read(u) arr_in ! read the array
print *, arr_in ! 1 2 3 4 5
close(u)
! Sequential unformatted (default) - with record markers
open(newunit=u, file="data_seq.bin", status="replace", &
form="unformatted") ! access defaults to "sequential"
do i = 1, 5
write(u) i, arr(i) ! each write is a "record"
end do
close(u)
! Read sequentially
open(newunit=u, file="data_seq.bin", status="old", &
form="unformatted", action="read")
do
read(u, iostat=i) n, arr_in(1)
if (i /= 0) exit
print *, n, arr_in(1)
end do
close(u)
end program binary_ioNamelist (I/O Agrupado)
namelist agrupa variáveis para I/O de texto estruturado — como JSON/YAML, mas nativo do Fortran. Defina com 'namelist /name/ var1, var2, ...'. write(u, nml=name) produz no formato &NAME var=val, var=val, /. read(u, nml=name) faz parse de volta. Namelist é perfeito para arquivos de configuração: usuários editam um arquivo de texto, o programa o lê. As variáveis mantêm seus valores declarados como padrão; apenas as que estão no arquivo são sobrescritas. O formato é tolerante (insensível a whitespace, vírgulas opcionais). Namelist suporta todos os tipos intrínsecos e arrays. iostat captura erros de parse. Esta é a maneira mais fácil de tornar um programa Fortran configurável sem escrever um parser personalizado.
program namelist_demo
implicit none
! Namelist groups variables for easy I/O
integer :: max_iter = 100
real :: tolerance = 1.0e-6
logical :: verbose = .true.
character(20) :: method = "newton"
real :: params(3) = [0.1, 0.2, 0.3]
! Define a namelist group
namelist /config/ max_iter, tolerance, verbose, method, params
! Write namelist to file
block
integer :: u
open(newunit=u, file="config.nml", status="replace")
write(u, nml=config)
close(u)
end block
! The file looks like:
! &CONFIG
! MAX_ITER=100,
! TOLERANCE=1.0000000E-06,
! VERBOSE=T,
! METHOD="newton",
! PARAMS=0.100000, 0.200000, 0.300000,
! /
! Read namelist from file (overrides defaults)
block
integer :: u, ios
open(newunit=u, file="config.nml", status="old", action="read")
read(u, nml=config, iostat=ios)
close(u)
if (ios /= 0) then
print *, "Error reading config"
else
print *, "max_iter=", max_iter
print *, "tolerance=", tolerance
print *, "method=", trim(method)
print *, "params=", params
end if
end block
! User can edit config.nml in a text editor
! then re-run to pick up new values
end program namelist_demoArquivos Internos & Tratamento de Erros
Arquivos internos permitem ler de / escrever para uma string de caracteres em vez de um arquivo — sprintf/sscanf do Fortran. write(str, fmt) formata para uma string; read(str, fmt) faz parse de uma string. Esta é a maneira padrão de converter entre strings e números. Sempre use iostat para tratamento de erros: 0 = sucesso, negativo = EOF, positivo = erro. Os labels legados end= e err= funcionam, mas iostat é mais limpo (sem goto). Para parse robusto, verifique iostat após cada read. I/O interno é ótimo para: construir strings de saída, fazer parse de entrada do usuário, converter valores de arquivos de configuração. A string age como um 'arquivo interno' — mesmas declarações de I/O, apenas um destino de string.
program internal_io
implicit none
character(100) :: buffer
integer :: n = 42
real :: x = 3.14159
integer :: ios
! Internal write: format to a string (like sprintf)
write(buffer, '(A, I0, A, F8.4)') "n=", n, " x=", x
print *, trim(buffer) ! n=42 x= 3.1416
! Internal read: parse from a string (like sscanf)
character(50) :: input = "100 3.14 hello"
integer :: a
real :: b
character(20) :: c
read(input, *, iostat=ios) a, b, c
if (ios == 0) print *, a, b, trim(c) ! 100 3.14 hello
! Robust number parsing with error handling
character(20) :: num_str = "3.14abc"
real :: val
read(num_str, *, iostat=ios) val
if (ios /= 0) then
print *, "Parse error: '", trim(num_str), "' is not a number"
else
print *, "Value: ", val
end if
! End-of-file and error handling on file reads
block
integer :: u
character(100) :: line
open(newunit=u, file="data.txt", status="old", action="read")
do
read(u, '(A)', iostat=ios) line
if (ios < 0) then
print *, "End of file"
exit
else if (ios > 0) then
print *, "Read error at line"
exit
end if
print *, trim(line)
end do
close(u)
end block
! err= and end= labels (legacy style)
block
integer :: u, val
open(newunit=u, file="nums.txt", status="old", action="read")
do
read(u, *, end=100, err=200) val
print *, val
end do
100 print *, "Reached EOF"
close(u)
goto 300
200 print *, "Read error!"
close(u)
300 continue
end block
end program internal_ioComputação Numérica
Parâmetros Kind & Precisão
Parâmetros kind controlam precisão/tamanho. Maneira moderna e portátil: use iso_fortran_env (int32/int64, real32/real64/real128). Legado: selected_real_kind(digits, exponent_range). real32 ≈ 7 dígitos significativos, real64 ≈ 15 dígitos (double), real128 ≈ 33 dígitos (quad). CRÍTICO: sempre anexe o sufixo kind a literais (3.14_dp, não 3.14) — caso contrário, o literal é parseado como precisão simples ENTÃO convertido, perdendo dígitos. precision() retorna dígitos significativos; range() retorna intervalo de expoente decimal. epsilon() dá o epsilon da máquina (menor incremento distinguível). tiny/huge dão min/max. Para computação científica, use real64 (double precision) por padrão.
program precision_demo
use iso_fortran_env, only: int32, int64, real32, real64, real128
implicit none
! Portable kind selection via iso_fortran_env
integer(kind=int32) :: i32 = 100
integer(kind=int64) :: i64 = 9223372036854775807_int64
real(kind=real32) :: r32 = 3.14159265_real32 ! single (~7 digits)
real(kind=real64) :: r64 = 3.14159265358979_real64 ! double (~15 digits)
real(kind=real128) :: r128 = 3.14159265358979_real128 ! quad (~33 digits)
! Legacy: selected_real_kind (still works)
integer, parameter :: dp = selected_real_kind(15, 307) ! double
integer, parameter :: sp = selected_real_kind(6, 37) ! single
real(kind=dp) :: pi = 3.14159265358979_dp
print *, "real32 precision: ", precision(r32), " range: ", range(r32)
print *, "real64 precision: ", precision(r64), " range: ", range(r64)
print *, "real128 precision: ", precision(r128)
! Epsilon and tiny
print *, "epsilon(r32): ", epsilon(r32) ! ~1.19e-7
print *, "epsilon(r64): ", epsilon(r64) ! ~2.22e-16
print *, "tiny(r64): ", tiny(r64) ! smallest positive
print *, "huge(i32): ", huge(i32) ! 2147483647
print *, "huge(i64): ", huge(i64)
! Always use _kind suffix on literals
! WRONG: real(kind=dp) :: x = 3.14 (loses precision!)
! RIGHT: real(kind=dp) :: x = 3.14_dp
end program precision_demoÁlgebra Linear (matmul, solve, BLAS)
Fortran tem álgebra linear built-in: matmul (multiplicação matriz-matriz/matriz-vetor), dot_product, transpose. Para resolver sistemas lineares (Ax=b), autovalores, SVD, etc., use LAPACK (a biblioteca Fortran padrão da indústria): dgesv resolve Ax=b, dgesvd faz SVD, dsyev faz decomposição de autovalores. Link com -llapack -lblas. O exemplo mostra eliminação Gaussiana manual para um sistema 3x3 — para trabalho real, use LAPACK (mais rápido, mais preciso com pivoteamento, lida com qualquer tamanho). O armazenamento column-major do Fortran corresponde às expectativas do LAPACK nativamente (sem transposição necessária). matmul é otimizado, mas para matrizes grandes, BLAS dgemm é mais rápido. Sempre verifique o número de condição para estabilidade numérica.
program linalg
implicit none
real(8) :: A(3,3), B(3,3), C(3,3)
real(8) :: v(3), w(3), x(3)
real(8) :: det
integer :: i
! Initialize matrices
A = reshape([1,2,3, 4,5,6, 7,8,10], [3,3]) ! column-major fill
B = reshape([1,0,0, 0,1,0, 0,0,1], [3,3]) ! identity
! Matrix-matrix multiplication
C = matmul(A, B) ! A * I = A
print *, "A * I = A? ", all(abs(C - A) < 1e-10)
! Matrix-vector multiplication
v = [1.0, 2.0, 3.0]
w = matmul(A, v)
print *, "A * v = ", w ! 14 32 53
! Dot product
print *, "v . v = ", dot_product(v, v) ! 14
! Outer product
C = 0.0
do i = 1, 3
C(:,i) = v * v(i)
end do
! Transpose
C = transpose(A)
! Solve linear system Ax = b (need LAPACK or custom)
! Using LAPACK dgesv:
! call dgesv(n, nrhs, A, lda, ipiv, b, ldb, info)
! For demo, manual Gaussian elimination:
x = solve_3x3(A, v)
print *, "Solution: ", x
contains
function solve_3x3(A, b) result(x)
real(8), intent(inout) :: A(3,3)
real(8), intent(in) :: b(3)
real(8) :: x(3), M(3,4), factor
integer :: k, j
M(:,1:3) = A
M(:,4) = b
! Forward elimination
do k = 1, 2
do i = k+1, 3
factor = M(i,k) / M(k,k)
M(i,:) = M(i,:) - factor * M(k,:)
end do
end do
! Back substitution
x(3) = M(3,4) / M(3,3)
x(2) = (M(2,4) - M(2,3)*x(3)) / M(2,2)
x(1) = (M(1,4) - M(1,2)*x(2) - M(1,3)*x(3)) / M(1,1)
end function
end program linalgNúmeros Aleatórios
call random_number(x) preenche x com reais uniformes [0,1) — funciona em escalares ou arrays. call random_seed(size=n) obtém o tamanho da seed; random_seed(put=seed) define a seed para reprodutibilidade (essencial para teste/debug). Para inteiros em [a,b]: a + int(r * (b-a+1)). Para números aleatórios Gaussianos (normais), use a transformação Box-Muller (mostrada) ou o método polar. Fortran não tem gerador de distribuição normal built-in — implemente ou use uma biblioteca. Para simulações de Monte Carlo, defina a seed para reprodutibilidade, então rode muitos testes. random_number NÃO é criptograficamente seguro — use uma biblioteca crypto para segurança. Para código paralelo, cada imagem precisa de uma seed distinta.
program random_demo
implicit none
real :: r
integer :: i, n = 10
real :: arr(10)
integer :: seed_size
integer, allocatable :: seed(:)
! Initialize random seed (Fortran 2003: random_seed with no args
! uses a processor-dependent seed; for reproducibility, set it)
call random_seed(size=seed_size)
allocate(seed(seed_size))
! Set a fixed seed for reproducibility
seed = [(12345 + i*6789, i=1, seed_size)]
call random_seed(put=seed)
! Generate uniform [0,1) random reals
call random_number(r)
print *, "Single: ", r
call random_number(arr) ! fills entire array
print *, "Array: ", arr
! Generate integers in [a, b]
block
integer :: a = 1, b = 6
integer :: dice
do i = 1, 5
call random_number(r)
dice = a + int(r * (b - a + 1)) ! [1, 6]
print *, "Dice roll: ", dice
end do
end block
! Normal (Gaussian) via Box-Muller transform
block
real :: u1, u2, z1, z2
integer :: j
do j = 1, 5
call random_number(u1)
call random_number(u2)
u1 = max(u1, 1e-10) ! avoid log(0)
z1 = sqrt(-2.0 * log(u1)) * cos(2.0 * 3.14159265 * u2)
z2 = sqrt(-2.0 * log(u1)) * sin(2.0 * 3.14159265 * u2)
print *, "Gaussian: ", z1, z2
end do
end block
! Monte Carlo: estimate pi
block
integer :: inside = 0, total = 100000
real :: x, y
do i = 1, total
call random_number(x)
call random_number(y)
if (x*x + y*y <= 1.0) inside = inside + 1
end do
print *, "Pi estimate: ", 4.0 * real(inside) / total
end block
end program random_demoIntegração Numérica & Busca de Raízes
Integração numérica: a regra de Simpson é mais precisa que a regra do trapézio (erro O(h^4) vs O(h^2)). Passe funções como argumentos usando um bloco interface. Busca de raízes: bisseção é robusta (sempre converge se o sinal muda) mas lenta (convergência linear); o método de Newton é rápido (convergência quadrática) mas precisa da derivada e pode divergir. Para trabalho de produção, use QUADPACK (integração) ou MINPACK (busca de raízes) — bibliotecas Fortran testadas em batalha. O bloco interface é essencial ao passar funções como argumentos — diz ao compilador a assinatura da função. Sempre defina uma contagem máxima de iterações para evitar loops infinitos. Verifique convergência com tolerâncias tanto do valor da função quanto do tamanho do passo.
program numerical
implicit none
real(8) :: a, b, result
integer :: n
! Numerical integration: Simpson's rule
! Integrate f(x) = x^2 from 0 to 2 (exact: 8/3 ≈ 2.6667)
a = 0.0; b = 2.0; n = 1000
result = simpson(f_sq, a, b, n)
print *, "Integral of x^2 from 0 to 2: ", result ! ~2.6667
! Integrate sin(x) from 0 to pi (exact: 2.0)
result = simpson(f_sin, 0.0_8, 3.14159265358979_8, 1000)
print *, "Integral of sin(x) from 0 to pi: ", result ! ~2.0
! Root finding: bisection method
! Find root of f(x) = x^2 - 2 (i.e., sqrt(2) ≈ 1.4142)
result = bisection(f_x2_minus_2, 0.0_8, 2.0_8, 1e-12_8)
print *, "sqrt(2) = ", result ! ~1.41421356
! Newton's method (needs derivative)
result = newton(f_x2_minus_2, fp_x2_minus_2, 1.0_8, 1e-12_8)
print *, "sqrt(2) via Newton: ", result
contains
! Function to integrate: x^2
function f_sq(x) result(y)
real(8), intent(in) :: x
real(8) :: y
y = x * x
end function
function f_sin(x) result(y)
real(8), intent(in) :: x
real(8) :: y
y = sin(x)
end function
! Simpson's rule: integral of f from a to b with n intervals
function simpson(f, a, b, n) result(integral)
interface
function f(x) result(y)
import
real(8), intent(in) :: x
real(8) :: y
end function
end interface
real(8), intent(in) :: a, b
integer, intent(in) :: n
real(8) :: integral, h, x
integer :: i
h = (b - a) / n
integral = f(a) + f(b)
do i = 1, n-1
x = a + i * h
if (mod(i, 2) == 0) then
integral = integral + 2.0 * f(x)
else
integral = integral + 4.0 * f(x)
end if
end do
integral = integral * h / 3.0
end function
function f_x2_minus_2(x) result(y)
real(8), intent(in) :: x
real(8) :: y
y = x*x - 2.0
end function
function fp_x2_minus_2(x) result(y)
real(8), intent(in) :: x
real(8) :: y
y = 2.0 * x
end function
! Bisection: find root in [a, b] (f(a) and f(b) must have opposite signs)
function bisection(f, a, b, tol) result(root)
interface
function f(x) result(y)
import
real(8), intent(in) :: x
real(8) :: y
end function
end interface
real(8), intent(in) :: a, b, tol
real(8) :: root, fa, fb, mid, fmid
integer :: iter, maxiter = 100
fa = f(a); fb = f(b)
if (fa * fb > 0) stop "No sign change in interval"
do iter = 1, maxiter
mid = (a + b) / 2.0
fmid = f(mid)
if (abs(fmid) < tol .or. (b - a)/2.0 < tol) then
root = mid
return
end if
if (fa * fmid < 0) then
b = mid; fb = fmid
else
a = mid; fa = fmid
end if
end do
root = (a + b) / 2.0
end function
! Newton's method: x_{n+1} = x_n - f(x)/f'(x)
function newton(f, fp, x0, tol) result(root)
interface
function f(x) result(y)
import
real(8), intent(in) :: x
real(8) :: y
end function
function fp(x) result(y)
import
real(8), intent(in) :: x
real(8) :: y
end function
end interface
real(8), intent(in) :: x0, tol
real(8) :: root, x, fx, fpx
integer :: iter, maxiter = 100
x = x0
do iter = 1, maxiter
fx = f(x); fpx = fp(x)
if (abs(fx) < tol) then
root = x
return
end if
x = x - fx / fpx
end do
root = x
end function
end program numericalAritmética IEEE & Exceções
O módulo ieee_arithmetic (Fortran 2003) fornece suporte a IEEE 754: Infinity, NaN (Not a Number), NaN sinalizador/silencioso, flags de exceção e modos de arredondamento. NaN NUNCA é igual a nada (incluindo a si mesmo) — use ieee_is_nan() para testar. Infinity resulta de overflow ou divisão por zero. Flags de exceção (ieee_divide_by_zero, ieee_overflow, ieee_underflow, ieee_inexact, ieee_invalid) rastreiam se ocorreram exceções — verifique com ieee_get_flag, limpe com ieee_set_flag. ieee_set_halting_mode controla se uma exceção para o programa. Modos de arredondamento afetam operações de ponto flutuante. Use isso para código numérico robusto: detecte NaN/Inf, trate exceções graciosamente e controle a precisão. Nota: -ffast-math no gfortran quebra a conformidade IEEE (não use para código que dependa desses recursos).
program ieee_demo
use ieee_arithmetic
implicit none
real :: a, b, c
logical :: flag
! IEEE special values
a = ieee_value(a, ieee_positive_inf) ! +Infinity
b = ieee_value(b, ieee_negative_inf) ! -Infinity
c = ieee_value(c, ieee_quiet_nan) ! NaN
print *, "Infinity: ", a ! Infinity
print *, "NaN: ", c ! NaN
print *, "Inf > 1e30: ", a > 1e30 ! T
print *, "NaN == NaN: ", c == c ! F (NaN is never equal!)
! Check for special values
print *, "is_nan(c): ", ieee_is_nan(c) ! T
print *, "is_finite(1.0): ", ieee_is_finite(1.0) ! T
print *, "is_inf(a): ", ieee_is_finite(a) ! F
! Operations producing special values
print *, "1.0/0.0: ", 1.0/0.0 ! Infinity (if -ffast-math off)
print *, "0.0/0.0: ", 0.0/0.0 ! NaN
print *, "sqrt(-1.0): ", sqrt(-1.0) ! NaN
! IEEE exception flags
call ieee_set_halting_mode(ieee_divide_by_zero, .false.) ! don't halt
b = 1.0 / 0.0 ! sets divide_by_zero flag, returns Inf
call ieee_get_flag(ieee_divide_by_zero, flag)
print *, "Divide by zero occurred: ", flag ! T
! Check and clear flags
call ieee_set_flag(ieee_all, .false.) ! clear all flags
b = 1.0 / 0.0
call ieee_get_flag(ieee_divide_by_zero, flag)
print *, "Flag after division: ", flag ! T
call ieee_set_flag(ieee_all, .false.) ! clear
! Rounding modes
call ieee_set_rounding_mode(ieee_nearest) ! default
call ieee_set_rounding_mode(ieee_down) ! round toward -inf
call ieee_set_rounding_mode(ieee_up) ! round toward +inf
call ieee_set_rounding_mode(ieee_to_zero) ! truncate
! Comparing NaN-safe
if (ieee_unordered(c, 1.0)) print *, "c is unordered (NaN)"
end program ieee_demoCoarrays & Paralelo
Declaração básica de coarray
Coarrays são o modelo paralelo built-in do Fortran (F2008). Cada 'image' é um processo paralelo. Declare com sufixo [*]. this_image() retorna o rank; num_images() a contagem. Acesse remoto com x[k]. sync all é uma barreira. Compiladores: gfortran (com -fcoarray=lib), ifort, Cray.
program coarray_hello
use iso_fortran_env, only: real64
implicit none
real(real64) :: x[*] ! coarray — one copy per image
integer :: me
me = this_image()
x = real(me, real64) ! local assignment
call co_sum(x, result_image=1) ! reduce sum to image 1
if (me == 1) print *, 'Sum =', x
sync all ! barrier
end programAcesso remoto e sincronização
Acesse coarray remoto com sufixo [k] — comunicação one-sided. Leituras e escritas são não-bloqueantes até sync. sync all é barreira global; sync images([1,2]) espera por images específicas. Seções críticas: lock/unlock com critical...end critical. Evite deadlock ordenando syncs de forma consistente.
program remote_access
implicit none
integer :: val[*], neighbor
integer :: me, n
me = this_image()
n = num_images()
val = me * 10
sync all ! ensure all writes complete
! read from neighbor (circular)
neighbor = merge(1, me + 1, me == n)
print *, 'Image', me, 'sees neighbor', neighbor, 'value', val[neighbor]
! write to image 1 from all
if (me /= 1) val[1] = val[me]
sync all
if (me == 1) print *, 'Image 1 received:', val
end programOperações coletivas
Coletivos: co_sum, co_min, co_max, co_broadcast. Eles operam em coarrays e reduzem/broadcast através de images. result_image especifica quem obtém a resposta (padrão: todos). source_image para broadcast. Sempre sincronize antes de ler resultados coletivos em outras images. Mais rápido que loops manuais com syncs.
program collectives
use iso_fortran_env, only: real64
implicit none
real(real64) :: local_sum, global_sum[*]
integer :: i
local_sum = 0.0_real64
do i = 1, 100
local_sum = local_sum + real(i * this_image(), real64)
end do
global_sum = local_sum
call co_sum(global_sum, result_image=1)
if (this_image() == 1) print *, 'Total:', global_sum
! other collectives: co_min, co_max, co_broadcast
call co_broadcast(global_sum, source_image=1)
end programTipos derivados coarray
Tipos derivados podem ser coarrays — todo componente é replicado por image. Acesse componentes remotos com p[k]%field. Componentes allocatable em coarrays requerem Fortran 2018+ (alguns compiladores podem não suportar). Para arrays de partículas, use type(particle), allocatable :: particles(:)[:].
program coarray_types
implicit none
type :: particle
real :: x, y, z
real :: mass
end type
type(particle) :: p[*]
integer :: me
me = this_image()
p%mass = real(me)
p%x = real(me) * 0.5
sync all
! access component of remote coarray
if (me == 1) print *, 'Image 2 mass:', p[2]%mass
end programCoarrays allocatable e teams
Coarrays allocatable são alocados em todas as images simultaneamente com sufixo [*]. Teams (F2018) dividem images em grupos independentes — cada um tem seu próprio this_image/num_images. form team cria teams; change team entra no escopo. Útil para paralelismo hierárquico. Suporte do compilador varia (ifort, gfortran 9+).
program alloc_coarrays
use iso_fortran_env, only: team_type
implicit none
integer, allocatable :: data(:)[:]
type(team_type) :: odd_team, even_team
integer :: me
me = this_image()
allocate(data(100)[*]) ! allocate on all images
! F2018 teams — split images into groups
form team(merge(1, 2, mod(me, 2) == 1), odd_team, even_team)
if (mod(me, 2) == 1) then
change team(odd_team)
! this_image() and num_images() now refer to team
data(this_image()) = me
end team
end if
deallocate(data)
end programInteroperabilidade com C
Básico de ISO_C_BINDING
iso_c_binding fornece kinds compatíveis com C (c_int, c_double, c_char, etc.). bind(C, name='...') expõe Fortran para C com um nome de símbolo específico. Strings C precisam de terminador c_null_char. O bloco interface declara a assinatura da função C. Compile C e Fortran separadamente, link juntos.
program c_interop
use iso_c_binding, only: c_int, c_double, c_char, c_null_char
implicit none
interface
subroutine c_print(msg) bind(C, name='c_print')
import :: c_char
character(kind=c_char), dimension(*) :: msg
end subroutine
end interface
call c_print('Hello from Fortran' // c_null_char)
end program
! Corresponding C:
! void c_print(const char* msg) { printf("%s\n", msg); }Passando arrays para C
Passe arrays via c_loc (obtém ponteiro C) e tipo c_ptr. Use atributo 'value' para argumentos escalares C (passados por valor, não referência). Arrays Fortran são column-major; C é row-major — transpose arrays 2D ou documente a convenção. c_f_pointer converte ponteiros C de volta para ponteiros Fortran.
program array_interop
use iso_c_binding, only: c_double, c_int, c_loc, c_f_pointer
implicit none
real(c_double), target :: arr(10)
integer(c_int) :: n
type(c_ptr) :: ptr
arr = [(real(i), i=1,10)]
ptr = c_loc(arr(1)) ! get C pointer
! call C function: void process(double* arr, int n);
call process_c(ptr, size(arr, kind=c_int))
interface
subroutine process_c(arr, n) bind(C, name='process')
import :: c_double, c_int, c_ptr
type(c_ptr), value :: arr
integer(c_int), value :: n
end subroutine
end interface
end programTipos interoperáveis com C
Tipos com bind(C) têm layout de memória compatível com C — necessário para passar structs para C. Apenas kinds compatíveis com C são permitidos (sem real/integer padrão). Sem componentes allocatable/pointer. Arrays char de comprimento fixo emulam strings C. A ordem importa — Fortran pode reordenar componentes sem bind(C).
module data_types
use iso_c_binding, only: c_double, c_int, c_char
implicit none
type, bind(C) :: point
real(c_double) :: x, y
integer(c_int) :: id
end type
type, bind(C) :: string_holder
character(kind=c_char, len=1) :: name(64)
end type
end module
! C equivalent:
! struct point { double x, y; int id; };
! struct string_holder { char name[64]; };Chamando Fortran a partir de C
Subroutines com bind(C, name='...') são chamáveis a partir de C por aquele nome. Use 'value' para escalares que C passa por valor; arrays são passados por referência (sem value). Resultados de função devem ser escalares compatíveis com C. Use nomes de binding C para evitar name mangling do compilador (underscores, mudanças de caso).
! Fortran:
module fmod
use iso_c_binding, only: c_double
implicit none
contains
subroutine square_array(arr, n) bind(C, name='square_array')
integer, value :: n
real(c_double), intent(inout) :: arr(n)
arr = arr ** 2
end subroutine
end module
! C caller:
! extern void square_array(double* arr, int n);
! double data[5] = {1, 2, 3, 4, 5};
! square_array(data, 5);Ponteiros de função C e callbacks
abstract interface declara uma assinatura de função C. procedure(iface) aceita uma função correspondente como argumento. Passe ponteiros de função C diretamente. c_funloc obtém o endereço C de um procedimento Fortran; c_f_procpointer converte um c_funptr de volta para um procedimento Fortran. Útil para callbacks estilo qsort.
module callbacks
use iso_c_binding, only: c_funloc, c_funptr, c_int
implicit none
abstract interface
function comparator(a, b) bind(C)
import :: c_int
integer(c_int), value :: a, b
integer(c_int) :: comparator
end function
end interface
contains
subroutine sort_with_c(arr, n, cmp) bind(C)
integer(c_int), value :: n
integer(c_int), intent(inout) :: arr(n)
procedure(comparator) :: cmp
! ... use cmp(a, b) to compare ...
end subroutine
end module
! C side:
! int descending(int a, int b) { return b - a; }
! sort_with_c(arr, n, descending);Orientação a Objetos (extends/final)
Extensão de tipo (herança)
Extensão de tipo = herança. 'extends(parent)' declara uma subclasse. 'class(T)' é polimórfico (aceita T ou qualquer extensão); 'type(T)' é exato. 'abstract' + 'deferred' = método abstrato (deve ser sobrescrito). 'contains' introduz procedimentos type-bound. Sobrescreva redeclarando o procedimento com o mesmo nome.
module shapes
implicit none
type, abstract :: shape
real :: x = 0, y = 0
contains
procedure(area_iface), deferred :: area
procedure :: move => shape_move
end type
abstract interface
function area_iface(this) result(a)
import :: shape
class(shape), intent(in) :: this
real :: a
end function
end interface
type, extends(shape) :: circle
real :: radius
contains
procedure :: area => circle_area
end type
contains
subroutine shape_move(this, dx, dy)
class(shape), intent(inout) :: this
real, intent(in) :: dx, dy
this%x = this%x + dx
this%y = this%y + dy
end subroutine
function circle_area(this) result(a)
class(circle), intent(in) :: this
real :: a
a = 3.14159 * this%radius ** 2
end function
end modulePolimorfismo e SELECT TYPE
select type faz discriminação de tipo em runtime em variáveis polimórficas. 'type is (T)' corresponde ao tipo exato; 'class is (T)' corresponde a T e extensões. Dentro do bloco, a variável é tratada como o tipo correspondido (acessa componentes específicos). Arrays polimórficos seguram tipos mistos via class(shape) — mas a alocação deve ser por elemento.
module polymorph
use shapes, only: shape, circle, rectangle
implicit none
contains
subroutine describe(s)
class(shape), intent(in) :: s
select type(s)
type is (circle)
print *, 'Circle radius:', s%radius
type is (rectangle)
print *, 'Rectangle:', s%width, 'x', s%height
class is (shape)
print *, 'Some shape at:', s%x, s%y
class default
print *, 'Unknown type'
end select
end subroutine
subroutine process_all(shapes)
class(shape), intent(in) :: shapes(:)
integer :: i
do i = 1, size(shapes)
call describe(shapes(i))
print *, 'Area:', shapes(i)%area()
end do
end subroutine
end moduleFinalizers e destrutores
Procedimentos final rodam automaticamente quando uma variável sai do escopo — como destrutores C++. Defina com 'final :: name'. Deve ser uma subroutine pegando TYPE (não class) — sem polimorfismo. Um tipo pode ter múltiplos finalizers (sobrecarregados por rank). Use para fechar arquivos, liberar memória, liberar recursos. Não pode falhar/levantar exceção.
module resources
implicit none
type :: file_handle
integer :: unit = -1
contains
final :: close_file
procedure :: open
end type
contains
subroutine open(this, filename)
class(file_handle), intent(inout) :: this
character(*), intent(in) :: filename
open(newunit=this%unit, file=filename, status='old')
end subroutine
subroutine close_file(this)
type(file_handle), intent(inout) :: this
if (this%unit /= -1) then
close(this%unit)
this%unit = -1
end if
end subroutine
end module
program demo
use resources
type(file_handle) :: f
call f%open('data.txt')
! ... use file ...
end program ! f goes out of scope -> close_file called automaticallyConstrutores e alocação
Interface genérico com o nome do tipo age como um construtor personalizado — sobrecarrega o construtor de estrutura padrão. Múltiplos procedimentos permitem conjuntos de argumentos diferentes. O construtor padrão (point(x=..., y=...)) ainda está disponível, a menos que seja sobrescrito. Alocação polimórfica: allocate(circle :: shape_var) cria um circle em uma variável class(shape).
module points
implicit none
type :: point
real :: x, y
contains
procedure :: norm
end type
interface point
procedure new_point
procedure new_point_polar
end interface
contains
function new_point(x, y) result(p)
real, intent(in) :: x, y
type(point) :: p
p%x = x
p%y = y
end function
function new_point_polar(r, theta) result(p)
real, intent(in) :: r, theta
type(point) :: p
p%x = r * cos(theta)
p%y = r * sin(theta)
end function
function norm(this) result(n)
class(point), intent(in) :: this
real :: n
n = sqrt(this%x**2 + this%y**2)
end function
end module
program use_points
use points
type(point) :: a, b
a = point(1.0, 2.0) ! cartesian
b = point(3.0, 0.5) ! polar (same name, different args)
print *, a%norm(), b%norm()
end programTipos abstratos e templates
Tipos abstratos não podem ser instanciados — apenas estendidos. Procedimentos deferred devem ser sobrescritos em subclasses concretas. class(*) é polimórfico ilimitado — segura qualquer tipo (use select type para recuperar). Esse padrão implementa classes base abstratas e interfaces. Containers concretos (list, stack, queue) estendem e implementam os procedimentos deferred.
module container
implicit none
type, abstract :: container
contains
procedure(add_iface), deferred :: add
procedure(size_iface), deferred :: size
procedure :: is_empty => container_is_empty
end type
abstract interface
subroutine add_iface(this, item)
import :: container
class(container), intent(inout) :: this
class(*), intent(in) :: item
end subroutine
function size_iface(this) result(n)
import :: container
class(container), intent(in) :: this
integer :: n
end function
end interface
contains
function container_is_empty(this) result(e)
class(container), intent(in) :: this
logical :: e
e = (this%size() == 0)
end function
end module
! Concrete subclass implements add/size
! class(*) allows storing any type (unlimited polymorphic)Tipos Derivados Parametrizados
Declaração básica de PDT
Parameterized Derived Types (PDTs, F2003) são como templates C++. Parâmetros 'kind' são compile-time (fixos por instância); parâmetros 'len' são runtime (podem ser diferidos com ':'). Aloque tipos deferred-length com allocate(type(params) :: var). PDTs habilitam containers genéricos type-safe sem truques de pré-processador.
module pdt_types
implicit none
type :: matrix(k, n, m)
integer, kind :: k = kind(1.0)
integer, len :: n, m
real(k) :: data(n, m)
end type
! kind parameters: compile-time (like template params)
! len parameters: runtime (like allocatable dimensions)
end module
program use_pdt
use pdt_types
implicit none
type(matrix(kind(1.0), 3, 3)) :: small ! fixed 3x3
type(matrix(kind(1.d0), :, :)), allocatable :: big ! deferred
small%data = 0.0
allocate(matrix(kind(1.d0), 100, 100) :: big)
big%data = 0.d0
deallocate(big)
end programPDT com parâmetro kind
Tipos parametrizados por kind permitem escrever um tipo que funciona para múltiplas precisões. O tipo de retorno da função usa this%k para corresponder. Note a sintaxe class(array_t(k=*)) para o procedimento — necessário para tipos kind-param. Suporte do compilador: gfortran 9+, ifort. Útil para bibliotecas que suportam precisão simples/dupla/quad.
module typed_array
implicit none
type :: array_t(k)
integer, kind :: k = kind(1.0)
real(k), allocatable :: data(:)
contains
procedure :: sum_values
end type
contains
function sum_values(this) result(s)
class(array_t(k=*)), intent(in) :: this
real(this%k) :: s
s = sum(this%data)
end function
end module
program test
use typed_array
type(array_t(kind(1.0))) :: float_arr
type(array_t(kind(1.d0))) :: double_arr
allocate(float_arr%data(10))
allocate(double_arr%data(10))
float_arr%data = 1.0
double_arr%data = 1.d0
print *, float_arr%sum_values()
print *, double_arr%sum_values()
end programPDT com parâmetro length
Tipos parametrizados por length carregam o tamanho como um parâmetro de tipo. fstr(5) e fstr(6) são tipos diferentes. Procedimentos usam fstr(*) para aceitar qualquer length. O tipo de resultado pode depender dos lengths de entrada (c tem length a%n + b%n). Útil para buffers de tamanho fixo, arrays de tamanho estático. Suporte de compilador limitado — teste completamente.
module fixed_string
implicit none
type :: fstr(n)
integer, len :: n
character(len=n) :: str
end type
contains
function concat(a, b) result(c)
type(fstr(*)), intent(in) :: a, b
type(fstr(a%n + b%n)) :: c
c%str = a%str // b%str
end function
end module
program test
use fixed_string
type(fstr(5)) :: a = fstr(5)('Hello')
type(fstr(6)) :: b = fstr(6)(' World')
type(fstr(11)) :: c
c = concat(a, b)
print *, c%str
end programArrays allocatable em PDT
PDTs podem conter componentes allocatable. O parâmetro kind propaga para tipos de componente (real(this%k)). move_alloc transfere alocação eficientemente (sem cópia). Use class(stack(k=*)) em procedimentos para aceitar qualquer kind. PDTs com componentes allocatable combinam tipagem genérica com sizing dinâmico.
module stack_t
implicit none
type :: stack(k)
integer, kind :: k = kind(1.0)
real(k), allocatable :: data(:)
integer :: top = 0
contains
procedure :: push
procedure :: pop
procedure :: is_empty
end type
contains
subroutine push(this, val)
class(stack(k=*)), intent(inout) :: this
real(this%k), intent(in) :: val
if (this%top == size(this%data)) then
block
real(this%k), allocatable :: tmp(:)
tmp = [this%data, val]
call move_alloc(tmp, this%data)
end block
else
this%top = this%top + 1
this%data(this%top) = val
end if
end subroutine
end moduleLimitações de PDT e workarounds
Suporte a PDT varia — parâmetros kind são amplamente suportados; parâmetros length menos. Para sizing em runtime, prefira componentes allocatable em vez de parâmetros len. Arrays PDT (type(matrix(4,4)) :: arr(10)) podem não funcionar em todos os compiladores. Teste no seu compilador alvo. Para máxima portabilidade, use pré-processador (#define) ou interfaces genéricas.
! Limitations of PDTs:
! - Not all compilers fully support len parameters
! - I/O of PDTs may not work as expected
! - Some F2003 features (e.g., PDT arrays) have spotty support
! Workaround: use allocatable components instead of len params
module alt_matrix
implicit none
type :: matrix_alt(k)
integer, kind :: k = kind(1.0)
real(k), allocatable :: data(:,:)
end type
contains
function make_matrix(k_, n, m) result(mat)
integer, intent(in) :: n, m
! kind must be compile-time constant — can't pass dynamically
! workaround: separate constructors per kind
end function
end module
! Best practice: use kind params (well-supported), avoid len params
! Use allocatable components for runtime sizing insteadSubmodules
Estrutura básica de submodule
Submodules (F2008) separam interface de implementação. O módulo declara a interface; o submodule fornece o corpo. Mudanças no corpo do submodule não acionam recompilação de dependentes — apenas mudanças de interface o fazem. Use 'module procedure' para implementar. Ótimo para bibliotecas grandes com compilação cara.
! parent_module.f90
module math_lib
implicit none
interface
module function integrate(f, a, b, n) result(s)
abstract interface
real function func_t(x)
real, intent(in) :: x
end function
end interface
procedure(func_t) :: f
real, intent(in) :: a, b
integer, intent(in) :: n
real :: s
end function
end interface
end module
! sub_math.f90
submodule(math_lib) math_impl
implicit none
contains
module procedure integrate
integer :: i
real :: dx, x
dx = (b - a) / n
s = 0.5 * (f(a) + f(b))
do i = 1, n-1
x = a + i * dx
s = s + f(x)
end do
s = s * dx
end procedure
end submoduleMúltiplos submodules por módulo
Um módulo pode ter múltiplos submodules — divida implementações entre arquivos. Cada submodule começa com 'submodule(parent) name'. Procedimentos no mesmo módulo parent podem se chamar. Isso habilita compilação incremental: edite um submodule, recompile apenas ele e link. Útil para módulos muito grandes.
! big_lib.f90
module big_lib
implicit none
interface
module subroutine sort(arr)
real, intent(inout) :: arr(:)
end subroutine
module function mean(arr) result(m)
real, intent(in) :: arr(:)
real :: m
end function
module function stddev(arr) result(s)
real, intent(in) :: arr(:)
real :: s
end function
end interface
end module
! sub_sort.f90 — one submodule
submodule(big_lib) sort_impl
contains
module procedure sort
! quicksort implementation
end procedure
end submodule
! sub_stats.f90 — another submodule
submodule(big_lib) stats_impl
contains
module procedure mean
m = sum(arr) / size(arr)
end procedure
module procedure stddev
block
real :: mu
mu = mean(arr) ! can call sibling module procedures
s = sqrt(sum((arr - mu)**2) / (size(arr) - 1))
end block
end procedure
end submoduleProcedimentos internos apenas em submodule
Submodules podem conter procedimentos helper privados não visíveis através da interface do módulo parent. Isso esconde detalhes de implementação enquanto os mantém co-localizados. Apenas implementações 'module procedure' são acessíveis via parent. Helpers permanecem internos — melhor encapsulamento do que colocar tudo no bloco contains do módulo.
module sparse_ops
implicit none
interface
module function sparse_multiply(a, b) result(c)
! ... sparse matrix types ...
real, allocatable :: c(:,:)
end function
end interface
end module
submodule(sparse_ops) sparse_impl
implicit none
contains
module procedure sparse_multiply
! call helper — invisible to module users
call validate_dimensions(a, b)
c = multiply_kernel(a, b)
end procedure
! private helper — not exposed via parent module
subroutine validate_dimensions(a, b)
! ...
end subroutine
function multiply_kernel(a, b) result(c)
! ...
end function
end submoduleHerdando variáveis de módulo
Submodules têm acesso às variáveis e tipos do módulo parent via host association. Eles podem ler e modificar estado em nível de módulo. Isso é útil para configuração que afeta o comportamento da implementação. Mudanças em variáveis de módulo ainda requerem recompilação do submodule (e dependentes).
module config
implicit none
integer :: verbosity = 0
interface
module subroutine log_message(msg)
character(*), intent(in) :: msg
end subroutine
end interface
end module
submodule(config) config_impl
implicit none
! submodule can access parent module's variables
contains
module procedure log_message
if (verbosity > 0) then
print *, '[LOG]', msg
end if
end procedure
end submodule
! Usage:
! use config
! verbosity = 1
! call log_message('Starting up')Benefícios de compilação do submodule
O benefício principal: mudanças de implementação em um submodule não acionam recompilação de código que apenas 'usa' o módulo parent. Apenas o submodule em si recompila. Isso acelera dramaticamente builds incrementais para projetos Fortran grandes. Reestruture implementações que mudam frequentemente em submodules; mantenha interfaces estáveis no parent.
! Without submodules:
! module big_mod
! ... 5000 lines of implementation ...
! end module
! ! Any change -> recompile big_mod + all users
! With submodules:
! module big_mod
! ! just interfaces (~200 lines)
! end module
! submodule(big_mod) impl
! ! 5000 lines of implementation
! end submodule
! ! Change impl body -> recompile only submodule
! ! Change interface -> recompile everything (unavoidable)
! Build system example (Makefile):
! big_mod.o: big_mod.f90
! impl.o: impl.f90 big_mod.o
! user.o: user.f90 big_mod.o ! NOT impl.o
! app: user.o impl.o big_mod.o
! $(FC) -o app user.o impl.o big_mod.oPonto Flutuante IEEE
Exceções e flags IEEE
ieee_exceptions fornece acesso a flags de exceção IEEE (overflow, underflow, divide_by_zero, invalid, inexact). ieee_get_flag lê; ieee_set_flag limpa. ieee_set_halting_mode controla se exceções param o programa. ieee_arithmetic fornece ieee_is_nan, ieee_is_finite, ieee_is_negative, etc. Sempre verifique flags após computações críticas.
program ieee_exceptions
use ieee_exceptions
use ieee_arithmetic, only: ieee_is_nan, ieee_is_finite
implicit none
real :: x, y
logical :: overflow_flag
call ieee_set_halting_mode(ieee_overflow, .false.)
x = huge(x) * 10 ! overflow -> Inf, no halt
call ieee_get_flag(ieee_overflow, overflow_flag)
print *, 'Overflow occurred:', overflow_flag
y = 0.0 / 0.0 ! NaN
print *, 'x is finite:', ieee_is_finite(x)
print *, 'y is NaN:', ieee_is_nan(y)
call ieee_set_flag(ieee_all, .false.) ! clear all flags
end programTratamento de NaN e Inf
Inf e NaN são valores especiais IEEE. NaN != NaN (use ieee_is_nan para testar). Inf propaga através de aritmética. Operações que produzem NaN: 0/0, Inf-Inf, 0*Inf, sqrt(-1). Compile com -ffpe-trap=invalid,zero,overflow para parar nesses casos (debugging). Código de produção deve verificar NaN após operações arriscadas.
program nan_inf
use ieee_arithmetic, only: ieee_value, ieee_nan, &
ieee_positive_inf, ieee_negative_inf
implicit none
real :: pos_inf, neg_inf, nan_val
pos_inf = ieee_value(0.0, ieee_positive_inf)
neg_inf = ieee_value(0.0, ieee_negative_inf)
nan_val = ieee_value(0.0, ieee_nan)
print *, 'Inf - Inf =', pos_inf - pos_inf ! NaN
print *, '0 * Inf =', 0.0 * pos_inf ! NaN
print *, 'Inf > 1e30:', pos_inf > 1e30 ! T
print *, 'NaN == NaN:', nan_val == nan_val ! F (always!)
! generate via arithmetic
pos_inf = 1.0 / 0.0 ! may halt without -fno-trapping-math
end programModos de arredondamento
IEEE suporta 4 modos de arredondamento: nearest (padrão), down, up, to zero. ieee_set_rounding_mode o altera em runtime. Útil para aritmética de intervalos (computar limites superior/inferior arredondando para cima/baixo). Afeta todas as operações FP subsequentes até ser alterado. Restaure para nearest quando terminar. Alguns compiladores otimizam assumindo nearest — use com cuidado.
program rounding_modes
use ieee_arithmetic, only: ieee_set_rounding_mode, &
ieee_nearest, ieee_down, ieee_up, ieee_to_zero
implicit none
real :: x, y, result
x = 1.0 / 3.0
print *, 'Nearest:', x
call ieee_set_rounding_mode(ieee_down)
result = 1.0 / 3.0
print *, 'Down:', result
call ieee_set_rounding_mode(ieee_up)
result = 1.0 / 3.0
print *, 'Up:', result
call ieee_set_rounding_mode(ieee_to_zero)
result = 1.0 / 3.0
print *, 'To zero:', result
call ieee_set_rounding_mode(ieee_nearest) ! restore default
end programInquiry de recursos IEEE
ieee_features e funções de inquiry do ieee_arithmetic permitem verificar quais recursos IEEE o compilador/plataforma suporta. ieee_support_nan, ieee_support_inf, ieee_support_rounding, ieee_support_datatype, etc. Útil para código portátil — degrace graciosamente em sistemas não-IEEE. A maioria dos sistemas modernos suporta todos os recursos.
program ieee_inquiry
use ieee_features
use ieee_arithmetic, only: ieee_support_nan, ieee_support_inf, &
ieee_support_rounding, ieee_nearest
implicit none
if (ieee_support_nan(1.0)) print *, 'NaN supported'
if (ieee_support_inf(1.0)) print *, 'Inf supported'
if (ieee_support_rounding(ieee_nearest, 1.0)) &
print *, 'Nearest rounding supported'
! Check halting mode
block
use ieee_exceptions
logical :: halting
call ieee_get_halting_mode(ieee_overflow, halting)
print *, 'Overflow halts:', halting
end block
end programControlando o ambiente FP
Controle o comportamento FP por seção: desabilite halting para código arriscado, verifique flags depois, restaure. ieee_all corresponde a todas as exceções. Padrão: limpe flags, rode computação, verifique flags, trate erros. Para produção, prefira verificações explícitas (ieee_is_nan) em vez de detecção baseada em flags — flags podem ser definidas por código não relacionado.
program fp_env
use ieee_arithmetic
use ieee_exceptions
implicit none
real :: a, b, c
! Save/restore FP state
block
use iso_fortran_env, only: real32
real(real32) :: saved_state
! (no portable save/restore in standard — use compiler intrinsics)
end block
! Disable halting for a risky section
call ieee_set_halting_mode(ieee_all, .false.)
call ieee_set_flag(ieee_all, .false.)
a = sqrt(-1.0) ! NaN, no halt
b = 1.0 / 0.0 ! Inf, no halt
if (ieee_is_nan(a)) then
print *, 'Got NaN, using fallback'
a = 0.0
end if
! Re-enable halting
call ieee_set_halting_mode(ieee_divide_by_zero, .true.)
call ieee_set_halting_mode(ieee_invalid, .true.)
end programDesempenho & Otimização
Ordenação de arrays e contiguidade
Arrays Fortran são column-major: a(i,j) e a(i+1,j) são adjacentes na memória. A ordem do loop importa: o loop mais interno deve iterar o primeiro índice. Ordem errada causa cache misses — desaceleração de 10x+. A sintaxe de array (sum, matmul) deixa o compilador otimizar. Use arrays contíguos (não ponteiros) para o melhor desempenho.
program array_perf
implicit none
real, allocatable :: a(:,:), b(:,:)
integer :: i, j, n
real :: s
n = 1000
allocate(a(n,n), b(n,n))
! GOOD: column-major access (Fortran is column-major)
do j = 1, n
do i = 1, n
s = s + a(i,j)
end do
end do
! BAD: row-major access (cache misses)
do i = 1, n
do j = 1, n
s = s + a(i,j)
end do
end do
! BEST: array syntax (compiler optimizes)
s = sum(a)
end programProcedimentos pure e elemental
Procedimentos pure não têm efeitos colaterais — o compilador pode otimizar, paralelizar e reordenar chamadas. Procedimentos elemental funcionam tanto em escalares quanto em arrays (auto-vetorizados). pure elemental combina ambos. Use-os para funções matemáticas. Restrições: sem I/O, sem modificação global, intent(in) para todas as entradas. Habilita melhor otimização que procedimentos regulares.
module math_ops
implicit none
contains
! pure: no side effects, enables optimization
pure function square(x) result(y)
real, intent(in) :: x
real :: y
y = x * x
end function
! elemental: works on scalars and arrays element-wise
pure elemental function clamp(x, lo, hi) result(y)
real, intent(in) :: x, lo, hi
real :: y
y = max(lo, min(x, hi))
end function
end module
program use_math
use math_ops
implicit none
real :: arr(100), scalar
arr = clamp(arr, 0.0, 1.0) ! applies to each element
scalar = clamp(1.5, 0.0, 1.0) ! also works on scalar
arr = square(arr) ! elemental too (if declared)
end programFlags de otimização do compilador
Comece com -O2 -march=native para produção. -O3 pode ajudar ou prejudicar — faça benchmark. -Ofast quebra conformidade IEEE (não use se NaN/Inf importam). -flto habilita inlining entre arquivos (compilação mais lenta, execução mais rápida). Use -fcheck=all e -ffpe-trap em builds de debug para capturar erros. Otimização guiada por perfil (-fprofile-use) dá 5-15% de speedup.
! gfortran optimization flags:
! -O0 no optimization (debug)
! -O1 basic optimization
! -O2 standard optimization (recommended)
! -O3 aggressive (may increase code size)
! -Ofast -O3 + -ffast-math (breaks IEEE compliance)
! -march=native use CPU's full instruction set
! -flto link-time optimization (cross-file inlining)
! -fopenmp enable OpenMP pragmas
! -fprofile-generate/use profile-guided optimization
! Example Makefile:
! FC = gfortran
! FFLAGS = -O2 -march=native -Wall -fcheck=all
! FFLAGS_RELEASE = -O3 -march=native -flto -fno-trapping-math
! FFLAGS_DEBUG = -O0 -g -fcheck=all -fbacktrace -ffpe-trap=invalid,zero
! ifort equivalents:
! -O2, -O3, -xHost (= -march=native), -ipo (= -flto)Paralelismo OpenMP
OpenMP adiciona paralelismo via diretivas (!$omp). 'parallel do' paraleliza o próximo loop. 'reduction(+:s)' lida com somas com segurança. Compile com -fopenmp (gfortran) ou -qopenmp (ifort). Defina a contagem de threads via variável de ambiente OMP_NUM_THREADS. Melhor para loops CPU-bound com iterações independentes. Cuidado com false sharing e desequilíbrio de carga.
program openmp_demo
use omp_lib
implicit none
real, allocatable :: a(:), b(:), c(:)
integer :: i, n, nthreads
n = 10000000
allocate(a(n), b(n), c(n))
a = 1.0; b = 2.0
!$omp parallel
if (omp_get_thread_num() == 0) then
nthreads = omp_get_num_threads()
print *, 'Threads:', nthreads
end if
!$omp end parallel
!$omp parallel do
do i = 1, n
c(i) = a(i) + b(i)
end do
!$omp end parallel do
! reduction
block
real :: s
s = 0.0
!$omp parallel do reduction(+:s)
do i = 1, n
s = s + c(i)
end do
!$omp end parallel do
print *, 'Sum:', s
end block
end programProfiling e detecção de hotspots
system_clock dá timing portátil. Para profiling sério, use gprof (compile com -pg), perf (Linux) ou Intel VTune. Faça profile antes de otimizar — surpresas são comuns. Concentre-se em hotspots (os 20% do código que tomam 80% do tempo). Otimize os loops mais internos primeiro. Sempre faça benchmark antes/depois das mudanças — intuição costuma estar errada.
! Compile with profiling:
! gfortran -pg -O2 prog.f90 -o prog
! ./prog (generates gmon.out)
! gprof prog gmon.out > profile.txt
! Or use perf (Linux):
! perf record ./prog
! perf report
! Or gprofng (modern):
! gprofng collect app ./prog
! gprofng analyze
! Manual timing:
program timing
use iso_fortran_env, only: real64, int64
implicit none
integer(int64) :: start, finish, rate
real(real64) :: elapsed
real, allocatable :: a(:)
integer :: i
allocate(a(10000000))
call system_clock(start, rate)
do i = 1, 100
a = a + 1.0
end do
call system_clock(finish)
elapsed = real(finish - start, real64) / real(rate, real64)
print *, 'Elapsed:', elapsed, 'sec'
end programProgramação em Linguagem Mista
Fortran chamando biblioteca C
Envolva funções de biblioteca C em um módulo de interface Fortran. Use bind(C, name='...') para linkar ao símbolo C exato. Passe ponteiros como c_ptr com 'value'. Para argumentos de ponteiro de função (como o comparador do qsort), use uma abstract interface. Isso permite chamar qualquer função C — incluindo libc, BLAS, system calls.
module c_glue
use iso_c_binding
implicit none
interface
function c_malloc(size) bind(C, name='malloc')
import :: c_ptr, c_size_t
integer(c_size_t), value :: size
type(c_ptr) :: c_malloc
end function
subroutine c_free(ptr) bind(C, name='free')
import :: c_ptr
type(c_ptr), value :: ptr
end subroutine
subroutine c_qsort(base, nmemb, size, compar) bind(C, name='qsort')
import :: c_ptr, c_size_t, c_int
type(c_ptr), value :: base
integer(c_size_t), value :: nmemb, size
abstract interface
function compar_fn(a, b) bind(C)
import :: c_ptr
type(c_ptr), value :: a, b
integer(c_int) :: compar_fn
end function
end interface
procedure(compar_fn) :: compar
end subroutine
end interface
end moduleInteroperação C++ via extern C
C++ faz name-mangling de símbolos, então envolva funções C++ em blocos extern "C". A interface Fortran então se vincula ao nome não mangled. Link com -lstdc++ (gfortran) ou use um linker C++. Isso permite usar bibliotecas C++ (STL, Boost, Qt) a partir do Fortran. Para classes C++, escreva um wrapper de API C flat, então chame a partir do Fortran.
! C++ side (wrapper.cpp):
! extern "C" {
! void cpp_process(double* data, int n) {
! std::vector<double> v(data, data+n);
! std::sort(v.begin(), v.end());
! std::copy(v.begin(), v.end(), data);
! }
! }
! Fortran side:
module cpp_glue
use iso_c_binding
implicit none
interface
subroutine cpp_process(data, n) bind(C, name='cpp_process')
import :: c_double, c_int
real(c_double), intent(inout) :: data(*)
integer(c_int), value :: n
end subroutine
end interface
end module
program use_cpp
use cpp_glue
implicit none
real(c_double) :: arr(10) = [5.d0, 3.d0, 8.d0, 1.d0, 9.d0, &
2.d0, 7.d0, 4.d0, 6.d0, 0.d0]
call cpp_process(arr, size(arr, kind=c_int))
print *, arr
end program
! Build: gfortran use_cpp.f90 wrapper.cpp -lstdc++ -o appInteroperação Python via f2py
f2py (parte do numpy) gera bindings Python automaticamente para Fortran. Ele lê atributos intent e cria interfaces Python/numpy adequadas. intent(out) torna-se um valor de retorno. Arrays mapeiam para arrays numpy (zero-copy quando contíguos). Ótimo para código numérico crítico de desempenho chamado a partir do Python. Suporte limitado para recursos F2003+.
! Fortran module (myfuncs.f90):
module myfuncs
implicit none
contains
subroutine compute(arr, n, result)
integer, intent(in) :: n
real(8), intent(in) :: arr(n)
real(8), intent(out) :: result
result = sum(arr**2)
end subroutine
function fast_sin(x) result(y)
real(8), intent(in) :: x
real(8) :: y
y = sin(x)
end function
end module
! Build Python extension:
! f2py -c myfuncs.f90 -m myfuncs
! (creates myfuncs.cpython-*.so)
! Python usage:
! import numpy as np
! import myfuncs
! arr = np.array([1.0, 2.0, 3.0])
! result = myfuncs.myfuncs.compute(arr)
! y = myfuncs.myfuncs.fast_sin(1.5)Biblioteca compartilhada e carregamento dinâmico
Use dlopen/dlsym (POSIX) ou LoadLibrary/GetProcAddress (Windows) para plugins carregados em runtime. Envolva em interfaces iso_c_binding. Converta c_ptr para ponteiro de procedimento com c_f_procpointer. Isso habilita arquiteturas de plugins — carregue diferentes implementações em runtime. Cross-platform: use #ifdef para loaders específicos de SO.
module dynamic_loader
use iso_c_binding
implicit none
interface
function dlopen(filename, flag) bind(C, name='dlopen')
import :: c_ptr, c_char, c_int
character(kind=c_char), dimension(*) :: filename
integer(c_int), value :: flag
type(c_ptr) :: dlopen
end function
function dlsym(handle, name) bind(C, name='dlsym')
import :: c_ptr, c_char
type(c_ptr), value :: handle
character(kind=c_char), dimension(*) :: name
type(c_ptr) :: dlsym
end function
function dlclose(handle) bind(C, name='dlclose')
import :: c_ptr, c_int
type(c_ptr), value :: handle
integer(c_int) :: dlclose
end function
end interface
end module
program plugin
use dynamic_loader
implicit none
type(c_ptr) :: lib, sym
integer, parameter :: RTLD_NOW = 2
lib = dlopen('./plugin.so' // c_null_char, RTLD_NOW)
if (.not. c_associated(lib)) then
print *, 'Failed to load library'
stop 1
end if
sym = dlsym(lib, 'init' // c_null_char)
! call init via c_f_procpointer...
print *, dlclose(lib)
end programIntegração com sistema de build
CMake lida bem com builds de linguagem mista. Declare todas as linguagens em project(). Defina flags por linguagem. Link bibliotecas na ordem de dependência. CMake rastreia dependências de módulos Fortran automaticamente (sem ordenação manual de .mod). Para linking da biblioteca padrão C++, use a flag certa por plataforma. Use Fortran como linker principal se o runtime Fortran for necessário.
# CMakeLists.txt for mixed Fortran/C/C++ project:
cmake_minimum_required(VERSION 3.18)
project(mixed LANGUAGES Fortran C CXX)
set(CMAKE_Fortran_FLAGS "-O2 -fopenmp")
set(CMAKE_C_FLAGS "-O2")
set(CMAKE_CXX_FLAGS "-O2 -std=c++17")
add_library(fortran_lib STATIC mymod.f90)
add_library(c_lib STATIC helper.c)
add_library(cpp_lib STATIC wrapper.cpp)
# Mixed-language executable
add_executable(app main.f90)
target_link_libraries(app fortran_lib c_lib cpp_lib)
# Fortran needs C runtime
if(APPLE)
target_link_libraries(app "-lc++")
else()
target_link_libraries(app stdc++)
endif()
# Module dependency tracking (CMake handles automatically)
# fortran_lib depends on its own .mod files
# app depends on fortran_lib's .mod filesI/O Moderno
Newunit e tratamento seguro de arquivos
newunit= evita colisões de número de unit — o compilador escolhe um número livre. Sempre verifique iostat após open/read/write — não-zero significa erro. iomsg dá uma mensagem de erro legível por humanos. action= ('read', 'write', 'readwrite') previne uso acidental. status= ('old', 'new', 'replace', 'scratch', 'unknown') controla criação de arquivo.
program safe_io
implicit none
integer :: u, ios
character(256) :: msg
! newunit: compiler picks an unused unit number
open(newunit=u, file='data.txt', status='old', action='read', &
iostat=ios, iomsg=msg)
if (ios /= 0) then
print *, 'Open failed:', trim(msg)
stop 1
end if
! read with error handling
read(u, *, iostat=ios, iomsg=msg) some_value
if (ios /= 0) then
print *, 'Read failed:', trim(msg)
end if
close(u)
contains
integer function some_value()
some_value = 0
end function
end programI/O Stream (binário)
Stream access (F2003) dá I/O orientado a bytes como C — sem record markers. access='stream' o habilita. pos= lê/escreve em um offset de byte específico. form='unformatted' para binário. Ótimo para arquivos binários interoperáveis (leia arquivos escritos em Fortran a partir de C/Python). Sequential access padrão usa record markers (não portátil).
program stream_io
implicit none
integer :: u, i
real, allocatable :: data(:)
! stream access = byte-oriented (like C fread/fwrite)
open(newunit=u, file='data.bin', access='stream', &
form='unformatted', status='replace')
data = [(real(i), i=1,100)]
write(u) data ! binary, no record markers
close(u)
! read back
allocate(data(100))
open(newunit=u, file='data.bin', access='stream', &
form='unformatted', status='old', action='read')
read(u) data
close(u)
! position-based access
open(newunit=u, file='data.bin', access='stream', &
form='unformatted', status='old')
read(u, pos=21) data(1) ! read 5th real (4 bytes each)
close(u)
end programI/O de tipo derivado
User-Defined Derived Type I/O (F2003) permite controlar como tipos são lidos/escritos. Defina uma subroutine com a assinatura específica e vincule-a via 'generic :: write(formatted)'. O código de formato DT a dispara. Habilita serialização personalizada (CSV, JSON-like, binário). iotype é 'LISTDIRECTED', 'NAMELIST' ou 'DT' para I/O formatado.
module person_type
implicit none
type :: person
character(20) :: name
integer :: age
real :: height
end type
contains
! custom formatted I/O via DT format
subroutine write_person(dtv, unit, iotype, v_list, iostat, iomsg)
class(person), intent(in) :: dtv
integer, intent(in) :: unit
character(*), intent(in) :: iotype
integer, intent(in) :: v_list(:)
integer, intent(out) :: iostat
character(*), intent(inout) :: iomsg
write(unit, '(a,",",i0,",",f0.2)', iostat=iostat) &
trim(dtv%name), dtv%age, dtv%height
end subroutine
end module
program use_dtv
use person_type
implicit none
type(person) :: p = person('Alice', 30, 5.7)
! DT format triggers custom writer
print "(DT)", p ! calls write_person
end programI/O Namelist
Namelist fornece I/O legível por humanos e baseado em nome para grupos de variáveis. Formato: &group_name var=value, ... /. A leitura apenas atualiza variáveis presentes no arquivo — outras mantêm seus valores. Ótimo para arquivos de configuração — usuários editam texto, sem parser necessário. Limitações: sem comentários em alguns compiladores, suporte de tipo limitado.
program namelist_demo
implicit none
integer :: n_iterations = 100
real :: tolerance = 1.0e-6
character(50) :: output_file = 'results.txt'
logical :: verbose = .true.
namelist /config/ n_iterations, tolerance, output_file, verbose
! write namelist
open(newunit=u, file='config.nml', status='replace')
write(u, nml=config)
close(u)
! read namelist (only specified vars are updated)
open(newunit=u, file='config.nml', status='old', action='read')
read(u, nml=config)
close(u)
print *, 'Iterations:', n_iterations
end program
! config.nml format:
! &config
! n_iterations = 500
! tolerance = 1.0e-8
! verbose = .false.
! /I/O assíncrono
I/O assíncrono (F2003) sobrepõe I/O com computação. write(..., asynchronous='yes') inicia uma operação não-bloqueante. wait(unit) bloqueia até completar. Útil para grandes conjuntos de dados — comece a escrever enquanto computa o próximo pedaço. Suporte do compilador varia. Use inquire(unit=u, pending=...) para verificar status. Combine com double-buffering para pipelines.
program async_io
implicit none
integer :: u1, u2, ios
real, allocatable :: a(:), b(:)
allocate(a(1000000), b(1000000))
a = 1.0; b = 2.0
open(newunit=u1, file='a.bin', access='stream', form='unformatted', &
asynchronous='yes')
open(newunit=u2, file='b.bin', access='stream', form='unformatted', &
asynchronous='yes')
! non-blocking writes
write(u1, asynchronous='yes') a
write(u2, asynchronous='yes') b
! do other work while I/O proceeds...
! wait for completion
wait(u1)
wait(u2)
close(u1); close(u2)
end programDepuração & Profiling
Flags de depuração em tempo de compilação
Use -fcheck=all para verificação de limites (captura erros off-by-one). -ffpe-trap para em NaN/Inf/overflow — inestimável para código numérico. -finit-real=nan torna variáveis não inicializadas visíveis (elas propagam como NaN). -fbacktrace imprime um stack trace em crash. Sempre depure com essas flags; remova para builds de produção (elas desaceleram o código).
! gfortran debug build:
! gfortran -O0 -g -fcheck=all -fbacktrace -ffpe-trap=invalid,zero,overflow \
! -Wall -Wextra -Wpedantic -finit-real=nan prog.f90
! Flag explanations:
! -O0 no optimization (easier debugging)
! -g debug symbols (for gdb)
! -fcheck=all bounds, pointer, recursion checks
! -fbacktrace print stack trace on error
! -ffpe-trap halt on FP exceptions (invalid, zero, overflow)
! -finit-real=nan initialize reals to NaN (catch uninitialized)
! -finit-integer=-9999 initialize integers to sentinel
! -Wall -Wextra more warnings
! Runtime error message:
! At line 42 of file prog.f90
! Fortran runtime error: Array bound out of bounds for dimension 1Tratamento de erros com iostat
iostat: negativo = EOF, zero = sucesso, positivo = erro. iomsg dá detalhes. Leia em uma string primeiro, então faça parse — separa erros de I/O de erros de parse. Rastreie números de linha para mensagens de erro úteis. Sempre trate erros explicitamente — falhas silenciosas são difíceis de depurar. Use stop com um código não-zero para sinalizar falha a scripts.
program robust_io
implicit none
integer :: u, ios, line_num
character(256) :: msg, line
real :: value
open(newunit=u, file='data.txt', status='old', action='read', &
iostat=ios, iomsg=msg)
if (ios /= 0) call error_exit('Open: ' // trim(msg))
line_num = 0
do
line_num = line_num + 1
read(u, '(a)', iostat=ios) line
if (ios < 0) exit ! EOF
if (ios > 0) then
print *, 'Read error at line', line_num
cycle
end if
read(line, *, iostat=ios, iomsg=msg) value
if (ios /= 0) then
print *, 'Parse error at line', line_num, ':', trim(msg)
cycle
end if
! process value
end do
close(u)
contains
subroutine error_exit(m)
character(*), intent(in) :: m
print *, 'ERROR:', m
stop 1
end subroutine
end programGDB para Fortran
GDB suporta Fortran: array slices, componentes de tipo derivado, procedimentos de módulo (formato de nome: modname__procname). Use -g -O0 para a melhor experiência de depuração. 'display' auto-imprime variáveis em cada parada — útil para observar loops. 'info locals' mostra todas as locais. Para variáveis de módulo, use 'print modname::varname'.
! Compile: gfortran -g -O0 prog.f90 -o prog
! Start: gdb ./prog
! Common GDB commands for Fortran:
! (gdb) break main break at main
! (gdb) break prog.f90:42 break at line 42
! (gdb) break mymod__my_sub break at subroutine (note double underscore)
! (gdb) run start program
! (gdb) next step over
! (gdb) step step into
! (gdb) print arr(5) print array element
! (gdb) print arr(1:10) print array slice
! (gdb) print mat(2,3) print 2D array element
! (gdb) print p%name print derived type component
! (gdb) display x watch variable (auto-print on stop)
! (gdb) backtrace show call stack
! (gdb) info locals show all local variables
! (gdb) continue resume executionProfiling com gprof
gprof amostra o program counter durante a execução. Compile TODOS os arquivos-fonte com -pg para profiles completos. O flat profile mostra onde o tempo é gasto; o call graph mostra a hierarquia de chamadas. Concentre-se em funções com alto tempo 'self' — é onde a otimização ajuda. Nota: -pg altera o timing — código profile-like pode se comportar diferente em produção.
! Compile with profiling:
! gfortran -pg -O2 prog.f90 -o prog
! Run:
! ./prog
! (creates gmon.out)
! Analyze:
! gprof prog gmon.out > profile.txt
! gprof prog gmon.out | less
! profile.txt sections:
! - Flat profile: time per function (self + cumulative)
! - Call graph: who called whom, how many times
! - Index: function cross-references
! Key columns:
! %time percentage of total time
! cumulative running total
! self seconds in function (excluding children)
! calls number of calls
! self/call average time per call (self)
! For multi-threaded: use gprofng (modern) or perfBenchmarking de desempenho
Faça benchmark adequadamente: warmup primeiro (efeitos de cache), rode múltiplas iterações, pegue o mínimo (menos ruído). system_clock com count_rate dá wall time. Reporte throughput (elementos/seg) para operações de array. Compare implementações na mesma máquina com as mesmas flags. Cuidado: o compilador pode otimizar resultados 'não usados' — use o resultado (ex.: imprima a soma) para prevenir isso.
program benchmark
use iso_fortran_env, only: real64, int64
implicit none
integer, parameter :: n = 1000000
integer, parameter :: niter = 100
real(real64), allocatable :: a(:), b(:), c(:)
integer(int64) :: start, finish, rate
real(real64) :: t_start, t_end, min_time
integer :: i, iter
allocate(a(n), b(n), c(n))
a = 1.0_real64; b = 2.0_real64
! warmup (cache, JIT-like effects)
do i = 1, n
c(i) = a(i) + b(i)
end do
min_time = huge(min_time)
call system_clock(count_rate=rate)
do iter = 1, niter
call system_clock(start)
do i = 1, n
c(i) = a(i) + b(i)
end do
call system_clock(finish)
t_start = real(start, real64) / rate
t_end = real(finish, real64) / rate
min_time = min(min_time, t_end - t_start)
end do
print *, 'Best time:', min_time * 1e6, 'us'
print *, 'Throughput:', real(n) / min_time / 1e9, 'G elem/s'
end programFortran Moderno
Free Form
Fortran moderno (90+) usa free form: sem restrições de coluna. Comentários começam com !. Statements podem abranger múltiplas linhas com &. Implicit none é obrigatório para segurança de tipo. Muito mais legível que fixed-form Fortran 77.
program modern
implicit none
integer :: i
do i = 1, 10
print *, "Value:", i
end do
end program modern
! Free form: no column restrictions
! Comments start with !Modules
Módulos agrupam procedimentos e dados relacionados. use importa um módulo. contains separa declarações em nível de módulo de procedimentos. Módulos fornecem interfaces explícitas, habilitando verificação de tipos. Prefira módulos a procedimentos externos.
module math_utils
implicit none
contains
function square(x) result(y)
real, intent(in) :: x
real :: y
y = x * x
end function square
end module math_utils
program test
use math_utils
print *, square(3.0) ! 9.0
end program testDerived Types
Tipos derivados são estruturas de dados definidas pelo usuário (structs). % acessa componentes. Procedimentos type-bound habilitam OOP. Construtores criam instâncias. Tipos derivados podem ter valores padrão e estender outros tipos (herança). Use para modelagem de dados complexos.
type :: Point
real :: x, y
end type Point
type(Point) :: p
p = Point(1.0, 2.0) ! Constructor
p%x = 3.0 ! Component access
print *, p%x, p%y
! Type-bound procedures (OOP)
type :: Circle
real :: radius
contains
procedure :: area => circle_area
end typeIntent Attributes
Parâmetros intent(in) são somente leitura (não podem ser modificados). intent(out) é somente escrita (definido pelo procedimento). intent(inout) é leitura-escrita. O compilador verifica violações de intent. Melhora clareza do código e habilita otimizações. Sempre especifique intent.
subroutine process(input, output, inout)
integer, intent(in) :: input ! Read-only
integer, intent(out) :: output ! Write-only
integer, intent(inout) :: inout ! Read-write
output = input * 2
inout = inout + 1
end subroutinePure & Elemental
Funções pure não têm efeitos colaterais (sem I/O, sem estado mutável). O compilador pode otimizá-las. Funções elemental funcionam tanto em escalares quanto em arrays automaticamente. Elas são pure por padrão. Ideais para operações matemáticas. Habilita execução paralela.
pure function square(x) result(y)
real, intent(in) :: x
real :: y
y = x * x
end function
! Elemental: works on scalars and arrays
elemental function double_it(x) result(y)
real, intent(in) :: x
real :: y
y = 2.0 * x
end function
! double_it([1,2,3]) returns [2,4,6]Arrays
Declaração de Array
Arrays Fortran são 1-indexed por padrão. Limites inferiores personalizados com (0:). Ordem column-major (primeiro índice varia mais rápido). Arrays allocatable são heap-allocated e devem ser deallocated. Constantes de array usam [ ]. Arrays Fortran são mais eficientes que arrays C devido a descriptors.
! Static arrays
real :: a(10) ! 1D, indices 1-10
real :: b(0:9) ! 1D, indices 0-9
real :: c(3, 4) ! 2D, 3 rows x 4 cols
! Allocatable (dynamic)
real, allocatable :: d(:)
allocate(d(100)) ! Allocate
deallocate(d) ! Free
! Array constants
integer :: nums(5) = [1, 2, 3, 4, 5]Operações de Array
Fortran suporta operações whole-array: +, -, *, /, **. Sem loops necessários para matemática elemento a elemento. Funções intrínsecas: sum, product, maxval, minval, any, all, count. Muito mais rápido que loops devido à vetorização. Esta é a força do Fortran para computação numérica.
real :: a(5) = [1, 2, 3, 4, 5]
real :: b(5)
b = a * 2 ! Element-wise: [2,4,6,8,10]
b = a + 1 ! [2,3,4,5,6]
print *, sum(a) ! 15
print *, maxval(a) ! 5
print *, any(a > 3) ! .true.
print *, count(a > 2) ! 3Seções de Array
Seções de array usam sintaxe (start:end:stride). :: significa padrão (1 ao fim, stride 1). Strides negativos revertem. Seções multi-dimensionais funcionam em qualquer dimensão. Seções podem ser passadas a procedimentos. Muito poderoso para slicing sem cópia.
real :: a(10) = [(i, i=1,10)]
print *, a(3:7) ! Elements 3 to 7
print *, a(::2) ! Every other: [1,3,5,7,9]
print *, a(2:8:2) ! Stride 2: [2,4,6,8]
real :: m(3,3)
m(:, 2) ! Second column
m(1, :) ! First rowConstruto where
where é atribuição condicional em nível de array. Como um if vetorizado. Elsewhere trata o caso falso. Mais eficiente que loops porque pode ser vetorizado. Use para operações condicionais elemento a elemento em arrays.
real :: a(5) = [1, -2, 3, -4, 5]
where (a > 0)
a = a * 2 ! Double positives
elsewhere
a = 0 ! Zero negatives
end where
! Result: [2, 0, 6, 0, 10]
! Equivalent to a loop with ifAlocação Dinâmica
Arrays allocatable são dimensionados dinamicamente. allocate cria, deallocate libera. Fortran 2003+ auto-desaloca no fim do escopo. Verifique status de alocação com allocated(). Arrays allocatable são automaticamente realocados na atribuição (Fortran 2003+). Muito mais seguro que C malloc/free.
real, allocatable :: matrix(:,:)
integer :: n
n = 100
allocate(matrix(n, n))
matrix = 0.0 ! Initialize all to 0
! ... use matrix ...
deallocate(matrix)
! Automatic deallocation at end of scope
! (Fortran 2003+)Operações de I/O
Saída Formatada
Strings de formato controlam a saída. I5 = integer largura 5. F8.3 = float largura 8, 3 decimais. I0 = largura mínima. A = string. X = espaço. / = nova linha. write(*,...) é o mesmo que print, mas mais flexível. Use strings de formato para saída alinhada.
integer :: i = 42
real :: x = 3.14159
print "(I5, F8.3)", i, x ! " 42 3.142"
print "(A, I0)", "Count=", i ! "Count=42"
write(*, "(3F6.2)") 1.0, 2.0, 3.0
! Format specifiers:
! I: integer, F: float, E: exponential
! A: string, X: space, /: newlineI/O de Arquivo
open conecta um arquivo a uma unit. newunit atribui um número de unit livre. status: old (deve existir), new (não deve existir), replace. iostat retorna não-zero em erro ou EOF. Sempre verifique iostat para evitar crashes. close desconecta o arquivo.
integer :: unit, ios
open(newunit=unit, file="data.txt", status="old", action="read")
do
read(unit, *, iostat=ios) value
if (ios /= 0) exit
print *, value
end do
close(unit)
! status: old, new, replace, scratch
! action: read, write, readwriteNamelist
namelist agrupa variáveis para I/O. O formato do arquivo é &config n=10, x=3.14 /. Útil para arquivos de configuração. Variáveis podem estar em qualquer ordem. Apenas variáveis listadas são lidas/escritas. Muito mais fácil que fazer parse de formatos personalizados.
integer :: n = 10
real :: x = 3.14
namelist /config/ n, x
! Write namelist
open(1, file="config.nml")
write(1, nml=config)
close(1)
! Read namelist
open(1, file="config.nml")
read(1, nml=config)
close(1)Arquivos Internos
Arquivos internos usam strings de caracteres como units de I/O. write para uma string converte valores para texto. read de uma string faz parse de texto. Útil para conversão de tipo e formatação. trim remove espaços à direita. Muito mais simples que C sprintf/sscanf.
character(20) :: str
integer :: num = 42
! Integer to string
write(str, "(I0)") num
print *, trim(str) ! "42"
! String to integer
read(str, *) num
! Internal files use character variables as unitsI/O Binário
I/O não-formatado escreve dados binários crus. Mais rápido e compacto que texto. access="stream" para acesso em nível de byte (Fortran 2008). Não portável entre arquiteturas (endianness). Use para grandes conjuntos de dados científicos. I/O formatado é para dados legíveis por humanos.
! Unformatted (binary) I/O
open(1, file="data.bin", form="unformatted", &
access="stream")
write(1) array ! No format, raw bytes
read(1) array2
close(1)
! Faster than formatted I/O
! Smaller file size
! Not portable across architecturesProgramação Paralela
OpenMP
OpenMP paraleliza loops com diretivas. !$omp parallel do distribui iterações entre threads. private: cada thread tem sua própria cópia. reduction: combina resultados. Compile com -fopenmp. Maneira fácil de paralelizar código numérico.
!$omp parallel do private(i) reduction(+:sum)
do i = 1, n
sum = sum + a(i) * b(i)
end do
!$omp end parallel do
! Compile: gfortran -fopenmp program.f90
! Environment: OMP_NUM_THREADS=4Coarrays
Coarrays (Fortran 2008) são arrays paralelos built-in. Cada image (processo) tem sua própria cópia. [N] acessa dados de outra image. sync all é uma barreira. this_image() retorna o número da image. Built-in na linguagem, sem biblioteca necessária.
program coarray_example
implicit none
integer :: me[*] ! Coarray: one per image
me = this_image()
sync all ! Barrier
if (this_image() == 1) then
print *, "Image 2 has:", me[2] ! Remote access
end if
end program
! Compile: gfortran -fcoarray=single program.f90Básico de MPI
MPI (Message Passing Interface) é o padrão para paralelismo distribuído. mpi_init/finalize começam e terminam. comm_rank dá o ID do processo. comm_size dá o total de processos. Send/recv para comunicação. Escala para milhares de cores. Use para clusters.
program mpi_example
use mpi
integer :: rank, size, ierr
call mpi_init(ierr)
call mpi_comm_rank(MPI_COMM_WORLD, rank, ierr)
call mpi_comm_size(MPI_COMM_WORLD, size, ierr)
print *, "I am rank", rank, "of", size
call mpi_finalize(ierr)
end program
! Compile: mpifort program.f90do concurrent
do concurrent (Fortran 2008) indica que iterações de loop são independentes. O compilador pode paralelizar automaticamente. local declara variáveis privadas. Mais seguro que OpenMP: o compilador verifica independência. Use para loops embarrassingly parallel.
do concurrent (i = 1:n) local(tmp)
tmp = expensive_compute(a(i))
b(i) = tmp * 2
end do
! Tells compiler iterations are independent
! Can be parallelized automatically
! local: private variable per iterationPadrão de Redução
Reduções combinam resultados parciais de cada thread. Comuns: sum, product, max, min. Cada thread computa um resultado parcial local. O runtime combina-os no final. Evita data races. Essencial para algoritmos numéricos paralelos.
!$omp parallel do reduction(+:total)
do i = 1, n
total = total + a(i)
end do
!$omp end parallel do
! Common reductions: +, *, max, min, .and., .or.
! Each thread has a private copy
! Combined at the endMétodos Numéricos
Álgebra Linear
Fortran tem operações de matriz built-in. matmul multiplica matrizes. dot_product computa produto escalar. transpose transpõe. Essas são altamente otimizadas (nível BLAS). Para produção, use LAPACK. Fortran é a linguagem de escolha para computação numérica de alto desempenho.
! Matrix multiplication
do i = 1, n
do j = 1, n
c(i,j) = sum(a(i,:) * b(:,j))
end do
end do
! Or use matmul intrinsic
c = matmul(a, b)
! Dot product
dot = dot_product(a, b)
! Transpose
at = transpose(a)Resolvendo ODEs
O método de Euler é o solver de ODE mais simples: y(n+1) = y(n) + dt*f(t,y). Para precisão, use Runge-Kutta (RK4). O bloco interface passa funções como argumentos. Fortran é ideal para computação científica devido a operações de array e desempenho.
! Euler method: dy/dt = f(t, y)
subroutine euler(f, t0, y0, dt, n, t, y)
interface
real function f(t, y)
real, intent(in) :: t, y
end function
end interface
real, intent(in) :: t0, y0, dt
integer, intent(in) :: n
real, intent(out) :: t(n+1), y(n+1)
integer :: i
t(1) = t0; y(1) = y0
do i = 1, n
t(i+1) = t(i) + dt
y(i+1) = y(i) + dt * f(t(i), y(i))
end do
end subroutineNúmeros Aleatórios
random_number gera reais uniformes [0,1). random_seed inicializa o gerador. Para inteiros, escale e converta. Box-Muller transforma uniforme em distribuição normal. Para trabalho sério, use uma biblioteca (ex.: Mersenne Twister). Sempre faça seed para reprodutibilidade.
call random_seed() ! Seed from system
call random_number(x) ! x in [0, 1)
! Array of randoms
real :: arr(100)
call random_number(arr)
! Integer in range [1, 6]
integer :: dice
call random_number(r)
dice = int(r * 6) + 1
! Normal distribution (Box-Muller)
call random_number(u1)
call random_number(u2)
z = sqrt(-2*log(u1)) * cos(2*PI*u2)Interpolação
Interpolação linear estima valores entre pontos conhecidos. Encontre o intervalo, então interpole. Para resultados mais suaves, use interpolação cubic spline. As operações de array do Fortran tornam isso conciso. Sempre verifique limites para evitar erros de extrapolação.
function interp(x, xs, ys) result(y)
real, intent(in) :: x, xs(:), ys(:)
real :: y
integer :: i
! Find interval
i = 1
do while (i < size(xs) .and. x > xs(i+1))
i = i + 1
end do
! Linear interpolation
y = ys(i) + (ys(i+1) - ys(i)) * &
(x - xs(i)) / (xs(i+1) - xs(i))
end functionIntegração Numérica
A regra do trapézio aproxima integrais: soma de trapézios. Mais preciso: regra de Simpson. Para dimensões maiores, use quadratura Gaussiana. Fortran se destaca em integração numérica devido ao desempenho. Sempre valide com soluções analíticas conhecidas.
! Trapezoidal rule
function trapezoid(f, a, b, n) result(integral)
interface
real function f(x)
real, intent(in) :: x
end function
end interface
real, intent(in) :: a, b
integer, intent(in) :: n
real :: integral, h
integer :: i
h = (b - a) / n
integral = (f(a) + f(b)) / 2
do i = 1, n-1
integral = integral + f(a + i*h)
end do
integral = integral * h
end functionArmadilhas Comuns
Indexação baseada em 1
Arrays Fortran são 1-indexed por padrão, diferente de C/Python (0-indexed). Isso causa erros off-by-one ao portar código. Limites inferiores personalizados (0:9) são permitidos. Seja consistente dentro de um projeto. Verifique limites de array com a flag de compilador -fcheck=bounds.
! Fortran arrays are 1-based by default
real :: a(10) ! Indices 1 to 10
! a(0) = 1.0 ! Error: out of bounds
a(1) = 1.0 ! OK
! Custom bounds
real :: b(0:9) ! Indices 0 to 9
b(0) = 1.0 ! OKTipagem Implícita
Sem implicit none, variáveis começando com i-n são integer, outras real. Isso causa bugs sutis (erro de digitação cria uma nova variável). Sempre use implicit none. Fortran moderno (2018+) pode defini-lo globalmente com -fimplicit-none. Esta é a prática recomendada mais importante do Fortran.
! BAD: implicit typing (Fortran 77 style)
program bad
! i-n start with integer by default
i = 1 ! integer (implicit)
x = 3.14 ! real (implicit)
end program
! GOOD: explicit typing
program good
implicit none ! Force explicit declaration
integer :: i
real :: x
end programOrdem Column-Major
Fortran armazena arrays em column-major: m(1,1), m(2,1), m(3,1), m(1,2), ... Acessar coluna-por-coluna é cache-friendly. Ordem errada de loop causa cache misses e desacelera em 10x+. Sempre corresponda a ordem do loop ao layout de memória. Oposto do C (row-major).
! Fortran is column-major (first index varies fastest)
real :: m(3, 3)
! Efficient: iterate over first index in inner loop
do j = 1, 3
do i = 1, 3
m(i, j) = 0.0 ! Cache-friendly
end do
end do
! Inefficient: row-major access
do i = 1, 3
do j = 1, 3
m(i, j) = 0.0 ! Cache misses
end do
end doPassagem por Referência
Fortran passa argumentos por referência (como ponteiros C). Subroutines podem modificar variáveis do chamador a menos que intent(in) seja especificado. Sem intent, modificações acidentais causam bugs. Sempre especifique intent. intent(out) sinaliza que o procedimento definirá o valor.
subroutine modify(x)
integer, intent(inout) :: x
x = 99 ! Modifies the caller variable
end subroutine
! Fortran passes by reference by default
! intent(in) prevents modification
! Without intent, modification is allowed (dangerous)Precisão de Ponto Flutuante
real padrão é precisão simples (~7 dígitos), frequentemente insuficiente. Use double precision para computação científica. kind(1.0d0) ou selected_real_kind(15) define double. Sempre adicione sufixo a literais: 3.14_dp. Misturar precisões causa truncamento silencioso. Use iso_fortran_env para kinds portáveis.
! Single precision (default)
real :: x = 3.14159 ! ~7 digits
! Double precision
real(kind=8) :: y = 3.14159d0 ! ~15 digits
! Or use kind parameter
integer, parameter :: dp = kind(1.0d0)
real(dp) :: z = 3.14159_dp
! Always use _dp or d0 for double literalsSnippets de Fortran relacionados
Copy-paste ready code for common tasks.
Arrays e Operações Vetoriais
Criar e operar com arrays em Fortran moderno.
Subrotinas e Funções
Definir procedimentos reutilizáveis em Fortran.
Módulos e Derived Types
Organizar código com módulos e tipos estilo OOP.
E/S de Arquivos e Formatação
Ler/escrever arquivos com saída formatada.
Paralelismo com OpenMP
Paralelizar loops com diretivas OpenMP.
Numérico: Álgebra Linear (BLAS/LAPACK)
Chamar BLAS/LAPACK para operações matriciais.
Derived Types e Pointers
Tipos customizados com componentes allocatable e pointers.
Pointers e Allocatables
Alocação dinâmica de memória em Fortran.
Was this helpful?