基础与程序结构
程序结构与 Hello World
每个 Fortran 程序以 'program NAME' 开头,以 'end program NAME' 结束。'implicit none' 在现代 Fortran 中是强制性的——它要求显式声明所有变量(如果不使用,Fortran 会采用隐式类型规则,以 i-n 开头的变量为整数,其他为实数,这是 bug 的主要来源)。'contains' 块将可执行代码与内部过程(在程序内部定义的子例程/函数)分开。注释以 '!' 开头。自由源码格式(Fortran 90+)使用 .f90 扩展名;列位置无关紧要。使用 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: ./hello变量与内建类型
Fortran 有 5 种内建类型:integer、real、complex、character、logical。'kind' 选择精度/大小——使用 kind=8 表示 64 位(或更好,使用 selected_real_kind/iso_fortran_env 以提高可移植性)。实数字面量需要 kind 后缀:3.14_8(不能只写 3.14)。Double precision 是 real(kind=8) 的旧语法。复数字面量使用 (real, imag) 形式。逻辑值是 .true. / .false.(带点)。字符串有固定的 'len',除非声明为 len=: 和 allocatable(延迟长度,Fortran 2003+)。始终使用匹配的 kind 后缀初始化,以避免静默精度损失。
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 variables常量与参数
常量使用 'parameter' 属性,必须在声明时初始化。它们不能被修改——编译器可以优化和内联它们。按约定使用 SCREAMING_SNAKE_CASE 命名。character(*) 表示'从初始化器获取长度'(对字符串常量很方便)。参数通常用于数组大小、物理常数和类似枚举的整数代码。Fortran 2003+ 也有正式的 ENUM 类型,但 parameter 整数仍然是惯用选择。参数可用于数组维度声明和其他常量表达式上下文中。
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 constants运算符与表达式
Fortran 运算符:算术(+ - * / **),其中 ** 表示幂运算(Fortran 特有)。整数除法向零截断——使用 real(a)/b 进行真除法。两种关系运算语法:现代(< > == /= <= >=)和旧式(.lt. .gt. .eq. .ne. .le. .ge.)。逻辑运算:.and. .or. .not. .eqv.(等价).neqv.(异或)。字符串连接使用 //;trim() 移除尾部空格(Fortran 用空格填充定长字符串)。mod 与 modulo:mod 遵循截断除法的符号,modulo 遵循向下取整除法——对负操作数结果不同。
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 operators内建函数与数学
Fortran 拥有丰富的内建(内置)函数。数学:abs、sqrt、exp、log(自然对数)、log10、sin/cos/tan/asin/acos/atan/atan2、sinh/cosh/tanh。取整:int(截断)、nint(最近)、floor、ceiling。转换:real()、int()、cmplx()。查询:size、shape、huge(最大值)、tiny(最小正值)、kind。所有三角函数使用弧度。atan2(y, x) 返回正确象限的角度(与 atan 不同)。使用 huge/tiny 检查范围限制。内建函数是元素级的——它们自动按元素作用于数组。
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 intrinsics控制流
If...Then...Else
块 IF:'if (cond) then ... else if (cond) then ... else ... end if'。每个分支需要 'then'(最后的 else 除外)。逻辑 IF 是单行形式:'if (cond) statement'(没有 'then'/'end if')。条件使用关系运算符(< > == /= <= >= 或 .lt. .gt. .eq. .ne. .le. .ge.)与 .and. .or. .not. 组合。'stop' 终止程序(可选带消息/代码)。算术 IF(if (x) label1, label2, label3)在 Fortran 2018 中已被删除——永远不要使用它。始终使用 'implicit none' 以捕获未声明的变量。
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 是 Fortran 的 switch 语句。case 可以是单个值(case (3))、列表(case (1, 3, 5))或范围(case (4:5) 表示 4 到 5,含端点)。case default 是回退分支。与 C 不同,没有 fall-through——每个分支独立,只运行一个。适用于 integer、character 和 logical 类型(不适用于 real)。字符范围使用 ASCII 排序('A':'Z')。对于浮点数比较,使用 if/else。对于整数/字符分派,select case 比长 if/else if 链更高效(编译器可能使用跳转表)。
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_demoDo 循环(计数)
计数 DO 循环:'do var = start, end, step'(step 默认为 1)。循环在 var <= end(正步长)或 var >= end(负步长)时运行。var 在每次迭代后递增。隐式 do 构造 [(expr, var=start,end)] 对数组初始化和 I/O 列表很强大。命名循环(outer: do ... end do outer)允许将 cycle/exit 指向特定的嵌套层级。循环变量自动定义;在 Fortran 中,循环结束后它保留最终值。避免在循环体内修改循环变量。
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 与无限循环
do while (cond) ... end do 是前置测试循环(每次迭代前检查条件;可能运行零次)。对于后置测试行为,使用 do ... if (cond) exit ... end do。裸 'do ... end do' 是无限循环——必须有 exit 语句(否则无限循环)。'exit' 退出最内层循环(或命名循环)。命名循环(factorial_loop:)让 exit 可以指向外层循环。当迭代次数未知且取决于条件时使用 do while;当次数预先知道时使用计数 do。
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 与循环控制
cycle 跳过当前迭代的剩余部分并跳到下一次迭代(类似 C/Python 中的 'continue')。exit 完全跳出循环(类似 'break')。两者默认针对最内层循环,但使用命名循环(search: do ... end do search)可以指向外层循环:'exit search' 或 'cycle search'。这对于干净地跳出嵌套循环至关重要。使用 cycle 进行过滤(跳过不需要的迭代),使用 exit 进行提前终止(找到搜索结果、检测到错误)。命名循环使嵌套控制流明确且可读。
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_control数组与向量运算
数组声明与初始化
Fortran 数组默认从 1 开始索引(下界 = 1),但可以指定自定义下界:a(0:4) 的索引为 0..4。多维数组使用 (rows, cols) 顺序——列主序存储(第一个索引在内存中变化最快)。使用数组构造器 [1,2,3] 或隐式 do [(expr, i=start,end)] 初始化。reshape 从 1D 列表填充多维数组。size() 返回总元素数;lbound/ubound 返回下/上界。shape() 以 1D 数组形式返回形状。数组是'整体数组'——可以无需显式循环就进行赋值和操作。
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_decl数组片段与向量下标
数组片段(切片)使用 a(start:end:stride) ��络语法——所有部分可选。步长可以为负(反向)。向量下标允许使用索引数组进行聚集/分散:a(idx) 返回 [a(idx(1)), a(idx(2)), ...]。片段可以赋值:a(2:4) = [99,98,97]。'where' 构造是数组级条件赋值(类似 numpy 的 where)。Fortran 的数组操作是向量化的——元素级运算无需显式循环。这是 Fortran 用于数值代码的杀手锏:干净、类似数学的语法,编译器自动向量化。
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_sections多维数组与矩阵
Fortran 以列主序存储数组(第一个索引在内存中变化最快)——与 C 相反。reshape 以列主序填充,所以 reshape([1,2,3,4],[2,2]) 得到 [[1,3],[2,4]]。matmul(A,B) 是真正的矩阵乘法(线性代数);A*B 是元素级(Hadamard)——它们不相同!transpose(A) 返回转置。归约:sum、product、maxval、minval、maxloc、minloc、count——都支持 dim= 沿一个轴归约。为获得高性能,以列主序编写循环(最内层循环遍历第一个索引)以对缓存友好。
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 matrices可分配数组(动态)
allocatable 数组是 Fortran 中做动态内存的现代方式——比指针更安全(无内存泄漏,作用域退出时自动释放)。使用 allocatable 属性和延迟形状(:、(:,:) 等)声明。allocate() 使用 stat= 捕获错误(始终检查!)。deallocate() 显式释放。Fortran 2003+ 支持赋值时自动重新分配:flex = [flex, 4] 扩展数组。allocated() 检查当前是否已分配。对于动态数组,allocatable 优于指针,因为编译器会跟踪并自动释放——无泄漏,无悬空指针。
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_demo数组内建函数
Fortran 的数组内建函数是其超能力。归约:sum、product、maxval、minval、maxloc(最大值索引)、minloc、count(true 的数量)、any(存在)、all(全部)。都支持 mask= 进行条件归约。pack() 收集 mask 为 true 的元素(类似 numpy 的 compress);unpack() 分散。cshift/eoshift 旋转数组(循环 vs 端点移出)。merge(a, b, mask) 进行元素级选择。注意:Fortran 没有内建排序——必须自己写(或使用库)。这些内建函数是元素级的且可向量化,使 Fortran 代码既干净又快速。
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_funcs字符串与字符处理
字符声明与长度
Fortran 字符串默认是定长的——较短的字符串用空格填充以填满声明的长度。character(N) 或 character(len=N) 声明长度为 N。character(*) 从上下文获取长度(parameter 初始化器或虚参)。character(:), allocatable 启用延迟长度动态字符串(Fortran 2003+)——字符串在赋值时重新分配。len() 返回声明长度;len_trim() 返回不含尾部空格的长度。trim() 返回不含尾部空格的字符串(但结果在上下文中仍是定长)。对于变长文本处理,使用 allocatable 延迟长度字符串。
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_decl字符串连接与操作
字符串连接使用 // 运算符。repeat(s, n) 将字符串重复 n 次。子字符串使用 s(start:end)——从 1 开始索引,两端都包含(与 Python 不同)。s(8:) 表示从位置 8 到末尾;s(:5) 表示从开头到位置 5。index(s, sub) 返回 sub 第一次出现的位置(未找到为 0,区分大小写)。scan(s, set) 返回集合中第一个字符的位置;verify(s, set) 返回不在集合中的第一个字符的位置。adjustl/adjustr 移动前导/尾部空格。Fortran 字符串不像 C 那样以 null 结尾——长度单独跟踪。
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_ops内建字符串函数
iachar(c) 返回字符的 ASCII 码;achar(i) 是逆操作。(ichar/char 依赖于处理器——为可移植性优先使用 iachar/achar。)Fortran 没有内建大小写转换——手动使用 iachar/achar(A-Z 是 65-90,a-z 是 97-122,差值是 32)。字典序比较:lge/lgt/lle/llt(字典序大于/小于)能优雅处理不同长度的字符串。内部 I/O(读/写到字符串而不是文件)是在字符串和数字之间转换的惯用方式:read(str, *) num 和 write(str, fmt) num。I0 格式给出最小宽度整数(无填充)。
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_funcs字符串格式化
格式规范位于字符串中:'(I5, F8.2, A)'。I=整数,F=定点实数,E=指数,ES=科学记数法(尾数 1-10),A=字符,X=空格,/=换行。宽度在前(I5 = 宽度 5),然后是可选的 .m 表示最小位数(I5.3)。I0 表示最小宽度(无填充)。ES 给出正确的科学记数法(1.23E+6),而 E 是(0.12E+7)。重复:3I4 = 三个整数每个宽度 4。格式中的字符串字面量:'("text")'。格式可以是字符串变量、*(表控,编译器选择)或标签(语句号)。为获得干净输出,整数优先用 I0,实数用 F 或 ES。
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_demo解析与分词
Fortran 没有内建的 split/分词——必须使用 index() 和子字符串手动编写。模式:用 index 找到分隔符,用子字符串提取标记,跳过分隔符,重复。对于 key=value 对,用 index 找到 '=' 并拆分为 key(之前)和 value(之后)。trim() 移除尾部空格;adjustl() 移除前导空格。为稳健解析,还要处理空标记和空白。或者,使用带格式说明符的内部读取处理结构化数据,或像从文件一样从字符串读取。某些 Fortran 框架中存在 split() 等库,但不是标准。
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: NYC过程:函数与子例程
函数
函数返回一个值并在表达式中使用(类似数学函数)。现代语法:'function name(args) result(var)'——result 变量是返回的内容。intent(in) 标记只读参数(编译器强制执行)。函数可以返回标量或数组(使用输入的 size() 来确定输出大小)。函数应该是 PURE 的(无副作用)——不要在函数中修改全局状态或做 I/O。内部过程(在 'contains' 块中)可以访问宿主的变量(宿主关联)。对于外部过程,使用 interface 块指定签名。
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_demo子例程与 Intent
子例程用 'call' 调用,不返回值——它们就地修改参数。在以下情况使用子例程:(1)需要修改多个参数,(2)操作是'命令'而非'计算',(3)返回数组值结果不方便。intent 属性记录并强制参数方向:intent(in) = 只读(赋值给它会编译错误),intent(out) = 只写(进入时未定义,返回前必须设置),intent(inout) = 读写。始终指定 intent——它捕获 bug 并启用优化。子例程可以修改传给它们的数组(如果连续则不制作副本)。
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_demo纯函数与元素函数
pure 函数没有副作用:无 I/O、无全局变量修改、无 stop,只能调用其他 pure 过程。它们启用编译器优化(并行化、公共子表达式消除),并在某些上下文中是必需的(如 DO CONCURRENT)。elemental 函数为标量编写,但自动按元素作用于数组——写一次,两者通用。pure elemental 结合两者。对任何真正的数学函数(无副作用)使用 pure。当操作自然地按元素应用于数组时(数学函数、转换)使用 elemental。编译器可以自动向量化数组上的 elemental 调用。
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_demo可选参数与关键字参数
optional 参数允许调用者省略它们。在过程内使用 present(arg) 检查是否提供了参数——访问缺失的 optional 是未定义行为。关键字参数(name="value")允许以任何顺序传递参数,并使调用自文档化。一旦使用关键字,所有后续参数也必须使用关键字。可选参数在签名中必须位于所有必需参数之后。默认值通过 present() 检查实现(Fortran 没有内建默认值语法)。关键字 + optional 一起启用灵活的 API:调用者只指定需要的。
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_demo内部与递归过程
内部过程(在 'contains' 内)具有宿主关联——它们可以读取和修改宿主程序的变量(类似闭包)。将它们用于需要宿主状态的辅助函数。递归过程必须用 'recursive' 前缀声明(Fortran 90/2003);Fortran 2018 使递归成为默认。对于相互递归,使用 interface 块声明前向引用。递归优雅但可能慢(函数调用开销)且有风险(深度递归的栈溢出)。对于阶乘/斐波那契,迭代版本更快更安全。对自然递归的问题(树遍历、分治)使用递归,且深度有界。
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_demo模块与封装
模块基础与使用
模块是 Fortran 的主要封装机制(替代 common 块和外部过程)。模块文件包含:(1)声明(常量、变量、派生类型),(2)带过程的 'contains' 块。使用 'use module_name' 导入;'use module_name, only: x, y' 只导入特定实体(推荐——避免命名空间污染)。模块变量是持久的(静态的)并由所有使用该模块的过程共享。模块提供显式接口(编译器检查参数类型),不同于外部过程。始终在使用模块的文件之前编译模块文件。模块中的 'implicit none' 会传播到其所有过程。
! 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 main访问控制(Public/Private)
访问控制:'private' 使实体成为模块内部;'public' 导出它们。默认可在模块级别设置('private' 然后选择性 'public :: ...')——这是最佳实践(显式接口)。派生类型组件可以是 private 的,即使类型本身是 public 的——调用者可以使用该类型但不能直接访问内部;必须通过过程。'save' 使模块变量持久(它们在调用之间保留值)——模块变量默认被保存。'final' 定义析构函数(对象超出作用域时调用)。这种封装通过过程强制不变量,实现真正的 OOP。
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_account模块中的派生类型
在模块中定义的派生类型可以有:类型绑定过程(procedure :: name => impl)、构造器(通过类型名称的重载 interface)和 allocatable 组件。类型绑定过程中的 'class(keyword)' 启用多态(实际类型可能是子类)。类型绑定过程以 obj%method(args) 调用——OOP 语法。将类型名称重载为 interface 允许有多个构造器(vector_from_array、vector_from_size)。allocatable 组件自动分配/释放。这是现代 Fortran OOP:封装、方法、构造器和多态,全部在模块系统内。
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_mod通用过程与重载
通用 interface 提供临时多态(重载):一个名称根据参数类型分派到不同的特定过程。'interface name / module procedure proc1, proc2 / end interface' 块列出所有特定过程。编译器在编译时根据参数类型/秩选择匹配的过程。通用中的所有特定过程必须有独特的签名(可通过参数类型区分)——否则歧义。这就是 Fortran 在没有模板的情况下实现运算符/函数重载的方式。通用分派在编译时解析(无运行时开销)。使用通用提供跨类型的统一 API。
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_generics运算符重载
运算符重载让你定义 +、-、*、/、== 等如何作用于派生类型。interface operator(+) / module procedure vec_add / end interface 将 + 运算符绑定到函数。对于二元运算符,可以用单独的过程重载两种顺序(vec*scalar 和 scalar*vec)。assignment(=) 重载赋值运算符(该过程是带 intent(out) LHS 和 intent(in) RHS 的子例程)。这启用类似数学的语法:c = a + b 而不是 c = vec_add(a, b)。对数学类型(向量、矩阵、复数)使用运算符重载,可提高可读性。避免对非显而易见的语义进行重载。结构构造器 vec3(x,y,z) 对派生类型是内建的。
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_ops派生类型(结构体)与 OOP
定义派生类型
派生类型是 Fortran 的结构体(用户定义的复合类型)。用 'type :: Name ... end type Name' 定义。组件用 % 访问(不是 .——那是用于复数的)。结构构造器:Name(val1, val2) 创建实例。组件可以有默认值(声明中的 = value)。整体类型赋值复制所有组件(对 allocatable 组件是深拷贝)。支持派生类型数组。派生类型是 Fortran OOP 的基础(带类型绑定过程、继承、多态)。使用 % 访问组件:obj%field、obj%method()。构造器语法 Name(args) 是自动的,除非用 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_types类型组件与构造器
派生类型可以有:allocatable 组件(自动管理内存)、默认初始化的组件、类型绑定过程(方法)和 finalizer(析构函数)。'interface TypeName / module procedure custom_init / end interface' 用自定义工厂函数重载结构构造器。类型绑定过程中的 'class(ClassName)'(vs 'type(ClassName)')启用多态(实际类型可能是子类)。'final' 过程在对象超出作用域时运行(析构函数)——用它们释放资源。allocatable 组件在终结时自动释放,但对于复杂清理,显式 finalizer 更清晰。block 构造允许在代码中间声明变量(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_components类型绑定过程(方法)
类型绑定过程是 Fortran 的方法:'procedure :: method_name => implementation'。以 obj%method(args) 调用——OOP 语法。第一个参数是 'self'(对象),声明为 'class(TypeName)'(多态)或 'type(TypeName)'(具体)。'class' 允许继承/多态;'type' 用于不可扩展类型。'=> implementation' 将方法名映射到特定过程(允许重命名)。final 是析构函数。此示例实现了一个带自动增长数组的动态栈。类型绑定过程提供真正的 OOP:封装(数据 + 方法在一起)、消息传递语法(obj%method)和多态(通过 class)。始终为类型绑定过程使用 class() 以启用未来继承。
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_stack继承与多态
Fortran OOP:'type, extends(Parent) :: Child' 创建子类(继承)。'type, abstract :: Name' 带 'procedure(...), deferred :: method' 定义抽象基类(类似 Java 抽象类 / C++ 纯虚函数)。'class(Base)' 是多态的——可以持有任何子类。多态分派:调用 obj%method() 调用子类的重写。'select type (var => expr) / type is (ConcreteType) / end select' 进行运行时类型检查(向下转型)。用 'allocate(TypeName::var)' 创建特定具体类型的多态对象。这是完整的 OOP:继承、多态、抽象类型和运行时分派——与 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_polymorphism嵌套类型与类型数组
派生类型可以嵌套(组合):一个类型可以有其他派生类型的组件。用链式 % 访问嵌套组件:emp%home%city。结构构造器自然嵌套:Employee(id, name, Address(...), salary)。支持派生类型数组:type(Employee) :: emps(N)。可以提取单个组件的数组:emps(:)%id 给出整数数组。allocatable 组件允许动态大小的集合(例如,有可变数量员工的部门)。这种组合模型是在 Fortran 中构建复杂数据结构(树、图、列表)的基础。当没有明确的子类型关系时,使用组合(has-a)而非继承(is-a)。
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_types文件 I/O 与格式化
打开与关闭文件
open() 将文件连接到单元号。newunit=u 让编译器选择唯一单元(避免冲突)——始终优先使用此方式而非硬编码单元号。status:'old'(文件必须存在)、'new'(必须不存在)、'replace'(删除 + 创建)、'scratch'(临时,关闭时自动删除)。action:'read'、'write'、'readwrite'。position:'rewind'(开头)、'append'(末尾)、'asis'(保持原位)。始终在 open 和 read 后检查 iostat——非零表示错误(负 = EOF,正 = 错误)。iomsg 给出描述性错误消息。close() 断开连接。为稳健的文件处理,始终检查 iostat 并优雅地处理错误。
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_open格式化 I/O
格式说明符控制 I/O:'(A, I0, F8.2)'。A=字符,I0=整数最小宽度,F8.2=实数宽度 8 带 2 位小数。write(unit, fmt) 写入;read(unit, fmt) 读取。unit=* 表示 stdout/stdin。对于文件,使用 open() 返回的单元。表控 I/O (*) 灵活:read(u, *) a, b, c 自动读取逗号/空格分隔的值。对于 CSV,如果值以逗号分隔,表控读取有效。格式字符串可重用:'(3I4)' 应用 I4 三次。始终使格式与数据类型匹配——不匹配的格式会导致运行时错误。对于混合文本+数字,先读为字符串再解析,或使用显式格式。
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_io无格式(二进制)I/O
无格式(二进制)I/O 比格式化(文本)I/O 更快更紧凑——无字符串转换。form='unformatted' 启用它。access='stream'(Fortran 2003)提供字节流访问(类似 C 文件 I/O,无记录标记)。access='sequential'(默认)使用记录标记(每次写/读是一个带长度前缀的记录)——在 Fortran 内可移植,但不能移植到其他语言。为了与 C/Python 互操作,使用流访问。二进制文件不可读,但非常适合大型数值数据集。始终在数据之前写入元数据(数组大小、类型信息),以便正确读回。无格式 I/O 保留完整精度(无文本转换的舍入)。
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)
namelist 为结构化文本 I/O 分组变量——类似 JSON/YAML 但是 Fortran 原生的。用 'namelist /name/ var1, var2, ...' 定义。write(u, nml=name) 以 &NAME var=val, var=val, / 格式输出。read(u, nml=name) 解析回来。namelist 非常适合配置文件:用户编辑文本文件,程序读取它。变量保留声明的值作为默认值;只有文件中的会被覆盖。格式宽容(对空白不敏感,逗号可选)。namelist 支持所有内建类型和数组。iostat 捕获解析错误。这是使 Fortran 程序可配置的最简单方式,无需编写自定义解析器。
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_demo内部文件与错误处理
内部文件允许从/向字符串而不是文件读写——Fortran 的 sprintf/sscanf。write(str, fmt) 格式化为字符串;read(str, fmt) 从字符串解析。这是在字符串和数字之间转换的标准方式。始终使用 iostat 进行错误处理:0 = 成功,负 = EOF,正 = 错误。旧式的 end= 和 err= 标签有效,但 iostat 更干净(无 goto)。为稳健解析,每次读取后检查 iostat。内部 I/O 非常适合:构建输出字符串、解析用户输入、转换配置文件值。字符串充当'内部文件'——相同的 I/O 语句,只是目标是字符串。
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_io数值计算
Kind 参数与精度
Kind 参数控制精度/大小。现代可移植方式:使用 iso_fortran_env(int32/int64、real32/real64/real128)。旧式:selected_real_kind(digits, exponent_range)。real32 ≈ 7 位有效数字,real64 ≈ 15 位(双精度),real128 ≈ 33 位(四倍)。关键:始终为字面量附加 kind 后缀(3.14_dp,不是 3.14)——否则字面量先被解析为单精度然后转换,丢失位数。precision() 返回有效位数;range() 返回十进制指数范围。epsilon() 给出机器 epsilon(最小可区分增量)。tiny/huge 给出最小/最大值。对于科学计算,默认使用 real64(双精度)。
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线性代数(matmul、solve、BLAS)
Fortran 有内建线性代数:matmul(矩阵-矩阵/矩阵-向量乘法)、dot_product、transpose。对于求解线性方程组(Ax=b)、特征值、SVD 等,使用 LAPACK(行业标准 Fortran 库):dgesv 求解 Ax=b,dgesvd 做 SVD,dsyev 做特征分解。用 -llapack -lblas 链接。示例展示了 3x3 系统的手动高斯消元——对于实际工作,使用 LAPACK(更快、带主元更准确、处理任何大小)。Fortran 的列主序存储原生匹配 LAPACK 的期望(无需转置)。matmul 已优化,但对于大矩阵,BLAS dgemm 更快。始终检查条件数以确保数值稳定性。
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 linalg随机数
call random_number(x) 用均匀 [0,1) 实数填充 x——适用于标量或数组。call random_seed(size=n) 获取种子大小;random_seed(put=seed) 设置种子以实现可重现性(对测试/调试至关重要)。对于 [a,b] 范围内的整数:a + int(r * (b-a+1))。对于高斯(正态)随机数,使用 Box-Muller 变换(所示)或极坐标法。Fortran 没有内建正态分布生成器——自己实现或使用库。对于蒙特卡洛模拟,设置种子以实现可重现性,然后运行多次试验。random_number 不是加密安全的——安全用途请使用加密库。对于并行代码,每个 image 需要不同的种子。
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_demo数值积分与求根
数值积分:辛普森法则比梯形法则更准确(O(h^4) 误差 vs O(h^2))。使用 interface 块将函数作为参数传递。求根:二分法稳健(如果符号变化总是收敛)但慢(线性收敛);牛顿法快(二次收敛)但需要导数且可能发散。对于生产工作,使用 QUADPACK(积分)或 MINPACK(求根)——经过实战检验的 Fortran 库。将函数作为参数传递时,interface 块是必不可少的——它告诉编译器函数的签名。始终设置最大迭代次数以避免无限循环。用函数值和步长容差检查收敛性。
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 numericalIEEE 算术与异常
ieee_arithmetic 模块(Fortran 2003)提供 IEEE 754 支持:无穷大、NaN(非数字)、信号/静默 NaN、异常标志和舍入模式。NaN 永远不等于任何东西(包括自身)——使用 ieee_is_nan() 测试。无穷大由溢出或除以零产生。异常标志(ieee_divide_by_zero、ieee_overflow、ieee_underflow、ieee_inexact、ieee_invalid)跟踪是否发生了异常——用 ieee_get_flag 检查,用 ieee_set_flag 清除。ieee_set_halting_mode 控制异常是否暂停程序。舍入模式影响浮点运算。将其用于稳健的数值代码:检测 NaN/Inf、优雅处理异常、控制精度。注意:gfortran 中的 -ffast-math 破坏 IEEE 合规性(不要用于依赖这些特性的代码)。