简明Fortran教程

这是一本简明的Fortran教程,其中包含了常见的Fortran 语法。具有C语言基础会使学习更加容易。

关于Fortran

Fortran是一门编译型编程语言,即将源代码编译链接成可执行程序,再去执行。Fortan最大的特点是快。

Fortran有两种格式,固定格式和自由格式。固定格式需要在行首输入特定的字符进行标识,并且每一行的长度有限制。而自由格式不需要在行首进行标识。

虽然现在编译器仍然支持固定格式,但推荐使用自由格式。

安装

建议新手使用带gfortran编译器的Code::Blocks,上手简单。

基础

字符集

符号数量 类型
26 大写字母 A-Z
26 小写字母 a-z
10 数字 0-9
1 下划线
5 运算符 + - * / **
28 其他符号 ( ) . = , ’ $ : ! " % & ; < > ? ~ \ [ ] ` ^ { }

程序结构

一个自由格式的程序示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PROGRAM my_first_program
! Purpose:
! To illustrate some of the basic features of a Fortran program.
!
! Declare the variables used in this program.
INTEGER :: i, j, k ! All variables are integers

! Get two values to store in variables i and j
WRITE (*,*) 'Enter the numbers to multiply: '
READ (*,*) i, j

! Multiply the numbers together
k = i * j

! Write out the result.
WRITE (*,*) 'Result = ', k
! Finish up.
STOP
END PROGRAM my_first_program

这个Fortran程序,就像所有的Fortan程序那样被分成了三段:

  1. 声明段。此部分由一组不可执行的语句组成,这些语句定义程序的名称和

    程序中引用的变量的数量和类型以及一些编译选项。

  2. 执行段。此部分由一组可执行的语句组成,这些语句被用来执行特定的任务。

  3. 结构段。此部分由一个或多个语句组成,该语句停止程序的执行,并告诉编译器程序执行完毕。

数据类型

Fortran是强变量类型的语言。

  • 变量类型必须在编译时确定,并且不允许改变。
  • 建议显式声明所有变量。
  • 如果未显式声明变量,则按照 IN 规则确定类型(ijklmn开头为整型,其他为实型)
  • IN 规则是个很讨厌的东西,因此,我们建议在每一个程序单元,使用 Implicit None
  • 变量有不同的类型,主要有整型(Integer)、实型(Real)、字符型(Character),还有 Complex 类型(本质上是real)、派生(type)类型(是其他类型的集合)
  • 整型无误差、实型有误差。字符型是接近人类的语言

Fortran 标准数据类型:

变量 数据类型
Integer(Kind=??) :: 整型
Real(Kind= ?? ) :: 实型 / 浮点型
Complex(Kind=??) :: 复数型(两个实型的组合)
Character(Kind=1,len=??) :: 字符型
Logical(Kind=??) :: 逻辑型 / 布尔型
Type( ?? ) :: 派生类型(上述类型的组合)

Kind

数据的Kind属性用以区分不同长度、精度或编码方式的同一种数据类型。Kind 受编译器的影响,具体数值可能会有差异。Kind 对不同的变量类型,表达的意思也不相同。

  • 对 Integer ,Kind 值影响整数能表达的最大范围;
  • 对 Real 和 Complex, Kind 值影响实数的最大范围和最小精度;
  • 对 Character ,Kind 值表示编码。通常为 ASCII 编码;
  • 对 Logical , Kind 值表示长度,对逻辑型无影响。

整型(Integer)

Integer 的 Kind,常见 1、2、4、8 等。

Kind 类型 最小值 最大值
1 超短整型 -(2^7) -128 2^7 -1 127
2 短整型 -(2^15) -32768 2^15-1 32767
4 整型 长整型 -(2^31) -2147483648 2^31-1 2147483647
8 超长整型 -(2^63) -9223372036854775808 2^63-1 9223372036854775807

对于大多数编译器,Kind 默认是 4,占有 4 个字节。但大多数编译器也允许调整默认Kind值。

并不是所有编译器都允许1、2、4、8的Kind值。某些编译器不支持Kind=8, Kind=1 ,而某些编译器用1、2、3、4 表示。

TODO:gfortran实验

可以用 k = Selected_Int_Kind( i ) 函数来选择能满足要求的Kind。 i 表示需要最大的十进制位数,k 表示返回的能满足范围的最小的Kind值。

1
2
3
4
5
6
7
8
9
10
11
12


Program www_fcode_cn
Implicit None
Integer , parameter :: KI = Selected_Int_Kind(9)
Integer(Kind=KI) :: i=0, j, k
write(*, *) kind(i) , i
write(*, *) huge(i)
i = i + 100_KI
write(*,*) i

End Program www_fcode_cn

整型和整型的计算结果依然是整型!

例如 1/2 = 0 ; 3/2 = 1

但整数和浮点数的计算结果一般是浮点数,例如 1/2.0 =0.5 。

实型/浮点型(Real)

Real 的 Kind,常见 4、8、16 等。

Kind 类型 最大值 最小值
4 实型 3.4028235E+38 1.1754944E-38
8 双精度 1.7976931E+308 2.2250738E-308
16 四精度 1.1897314E+4932 3.3621031E-4932

对于大多数编译器,Kind 默认是 4,占有 4 个字节。但大多数编译器也允许调整默认Kind值。

并不是所有编译器都允许4、8、16的Kind值。某些编译器不支持Kind=16 ,而某些编译器用1、2、3 表示。

1
k = Selected_Real_Kind( r , p ) 

可以用这个函数来选择能满足要求的Kind。r 表示需要最大的十进制位数 , p 表示最小的有效位数。k 表示返回的能满足范围的最小的Kind值。

由于浮点数存在误差,因此我们应该尽量避免以下操作:

  1. 对浮点数进行相等判断!

    1
    if ( a == 1.3 ) => if( abs(a-1.3)<1.0e-5 )
  2. 用浮点数做为数组角标!

    1
    b = a(2.0) => b = a(2)
  3. 用浮点数做为循环变量!

    1
    2
    Do r = 0.0 , 2.0 , 0.1 =>   Do i = 0 , 20
    r = i / 10.0

在数值计算中,我们应避免误差的放大和积累,因此设计合理、健壮的算法尤为重要。

复数型(Complex )

Complex 的数据类型与 Real 一致。

但需要注意的是:

如果 Kind=8 的 Real 占用 8 字节,则 Kind=8 的 Complex 占有 16 字节。

若使用 Complex(Kind=16) 来定义双精度,但实际上已经是四精度。

Complex 选择Kind,也使用 Selected_Real_Kind 函数。

字符型(Character )

Character 的 Kind,常见只有一种,即 1 表示 ASCII 编码。

也可以使用 Selected_Char_Kind 来选择。

1
k = Selected_Char_Kind( 'ASCII' )

字符串定义时必须有长度,且通常是固定的。

Fortran的字符串没有结束符 \0,所以在字符串操作中,必须注意!

有必要时需实用 trim 去除尾部空格。

可通过内部文件在字符串和整型、实型间相互转化。

1
2
3
read(字符串S , *)   整型或实型变量I  !  字符串->数字

write(字符串S , *) 整型或实型变量I ! 数字->字符串

字符型允许使用“子字符串”进行索引切片。

1
2
3
4
5
6

character(len=12) :: c = "www.fcode.cn"
c(5:9) = "fcode" !// 此时 c="www.fcode.cn"
c(:3) = "bbs" !// 此时 c="bbs.fcode.cn"
c(10:) = "" !// 此时 c="bbs.fcode___"

灵活运用子字符串可以让很多事情事半功倍。

逻辑型(Logical)

Logical 的 Kind,通常与该编译器支持的 Integer 一致。

变量声明

Fortran数据除了有类型之外,还有一定的属性,比如Kind。他们用定义时的形容词来赋予。

Fortran变量名必须以字母开头,且不能太长。

Fortran声明变量的基本格式:

类型( 属性 ) , 形容词 , 形容词 … :: 变量名(数组外形)= 值 , 变量名2(数组外形)= 值

下面是一些例子:

1
2
3
4
5
6
7
Real(Kind=8) , parameter , private :: rVar = 20.0d0

Character(Len=32) , Intent( IN ) :: cStr(5,8)

Integer , save :: n = 30 , m = 40

Integer m

运算符与表达式

运算符 含义
+
-
*
/
** 乘方

在写复合表达式时,应注意运算符的优先级。

关系运算符

关系运算符对照表(来源:中科大网站)

逻辑运算符

逻辑运算符对照表(来源:中科大网站)

输入与输出

Write()

1
Write(*,*) "Hello World! "

Write(*, *)中第一个星号代表使用默认输出位置,即屏幕,第二个星号代表不特别设置输出格式。

格式化输入输出

1
write(*,'(1i5)') 12345

控制流

条件分支

IF语句

IF语句常见的简单结构:

1
2
3
4
IF (Expression) THEN
...
...
END IF

或者:

1
2
3
4
5
6
7
IF (ConditionA) THEN
...
ELSE IF (ConditionB) THEN
...
ELSE
...
END IF
  • IF结构中的逻辑表达式的结果必须是一个单值,不能是数组。
  • 数组比较ANY, ALL等函数转换

SELECT CASE语句

SELECT CASE语句的结构:

1
2
3
4
5
6
7
8
SELECT CASE (keywd)
CASE(A)
...
CASE(B)
...
CASE DEFAULT
...
END SELECT

一般情况下,SELECT CASE语句可以与IF语句进行等价转换。

循环

DO循环

DO循环语句的结构如下:

1
2
3
DO i=min, max, step
...
END DO

DO WHILE循环

DO WHILE循环语句的结构如下:

1
2
3
DO WHILE (Expression)
...
END DO

循环控制

  • CYCLE:忽略本轮循环剩余内容,直接进入下一轮循环。
  • EXIT: (用于循环时)忽略循环剩余内容,跳出(指定)循环。

数组

数组的基本使用

数组的定义

1
2
3
Integer :: a(10) ! 定义1*10的数组
Integer :: a(10,10) ! 定义10*10的数组
Integer :: a(10,10) ! 定义10*10的数组

数组的操作

数组的索引从1开始。

赋初值

Data赋值

1
2
3
Integer :: a(5) ! 定义1*5的数组
Data a /1, 2, 3, 4, 5/ ! 依次赋值
Data a /3*5/ ! 表示这里有5个3
隐式循环

一个隐式循环的例子如下:

1
2
3
4
Integer :: a(10)
Integer :: i
...
a = [(i,i=1,10)]

对整个矩阵的操作

1
2
3
4
5
!a,b,c是三个相同大小的矩阵。b和c相同位置的相加,再赋值给a中的相同位置。-,*,/操作类似。
a = b+c

! 求矩阵中每一个元素的sin值。
a = sin(b)

索引切片

1
2
3
4
5
!a(3),a(4),a(5)设置成5
a(3:5)=5

!a(3)=3,a(4)=4,a(5)=5
a(3:5)=(/3,4,5)/
where
forall

可变大小的数组

1
2
3
4
5
6
!声明一个可变大小的数组
Integer,allocatable :: a(:)

write(*, *) "How many students:"
read(*, *) students
allocatable(a(students)) !给数组分配空间

函数与子程序

子程序(subroutine)

子程序拥有独立的变量声明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
program subroutine_ex1
implicit none
call sub1()
call sub2()
stop
end program subroutine_ex1

subroutine sub1()
implicit none
write(*,*) "This is sub1"
call sub2()
return
end subroutine sub1

subroutine sub2()
implicit none
write(*,*) "This is sub2"
return
end subroutine sub2

函数(function)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
program function_ex1
implicit none
real :: a=1
real :: b=2
real,external :: add ! 声明add是个函数而不是变量
!调用函数add,不必使用call命令
write(*,*) add(a,b)
stop
end

real function add(a,b)
implicit none
real :: a,b
! 这里是在声明函数会返回的数值类型
! real :: add

add = a + b
return
end

匿名函数

写法简单,但只能在本程序内使用。

1
2
3
4
5
6
7
8
9
program function_ex2
implicit none
real :: a=1
real :: b=2
real add
add(a,b)= a+b
write(*,*) add(a,b)
stop
end

文件操作

实例:写入矩阵到txt

1
2
3
4
5
6
7
8
9
10
real :: a(n,m)
a = 1.
open(111, file="example.txt")
do i= 1, n
do j=1, m
! 写入矩阵
write(1, '(1F20.5)',advance='no') a(i,j)
end do
write(*,*)
end do

标准数学函数

1
exp()  sqrt() sin() cos() tan() ...

简明Fortran教程
https://guixinliu.github.io/2021/08/15/fortran-introduction/
发布于
2021年8月15日
许可协议