原文: Fortran Cookbook:收集Fortran的奇淫技巧
本人准备写一本关于Fortran语言技巧的资料, 草稿提纲如下:
- 数组
- 整体操作
- 数组切片
- 变量声明
- ::
- 动态内存管理
- 指针
- 自定义类型
- 重载
- 文件操作
- open
- 创建临时文件
- 读写二进制流文件
- 检查IO函数返回值
- 检查IO函数出错指示
- 检查文件尾指示
- 异步IO
- close
- rewind
- fseek
- inquire
- endfile
-
打印、格式化
-
命令行参数
- 多进程、多线程
- OpenMP
- MPI
- GlobalArray
-
常用内置函数
- 浮点计算
- 精度控制
- 使用浮点异常
-
字符串
- C函数之间互调
- iso_c_binding
- C结构和Fortran TYPE
- Matlab和Fortran
- 调试
- namelist
- 预处理
- stop语句
- 特色控制语句
- CASE
- FORALL
- WHERE
- 程序结构
- 模块
- 函数
- 内置函数
- main函数
常用内置函数
- 浮点和整数之间的转换: 取整, 截断函数
- 浮点数截断取整: AINT(X), INT(X). 类似C语言的(int)(x). AINT返回浮点数, INT返回整数, 例如, AINT(-3.2)返回浮点数-3.0, INT(-3.2)返回整数-3
- 浮点数四舍五入: ANINT(X), NINT(X). 类似C语言round函数. ANINT返回浮点表示, NINT返回整数, 例如, ANINT(-3.7)返回-4.0, NINT(-3.7)返回-4
- FLOOR(X), CEILING(X). 等同于C函数floor和ceil
- MOD(A,P), MODULO(A,P). MOD(A,P)相当于A-INT(A/P)P; MODULO(A,P)对整数相当于C语言的A%P, 对浮点数相当于A-FLOOR(A/P)P.
- 几个数学函数
- SIGN(X, Y), 符号提取函数. 可用SIGN(X,Y).EQ.X判断X,Y是否同号
- ATAN2(Y,X), 求点(X,Y)所在象限角度. 有人用ATAN来求角度, 之后通过X,Y的符号确定(X,Y)在那个象限, 其实直接用ATAN2最方便, 效率也最高.
- RANDOM_NUMBER(A), 生成一个或多个随机变量. 如果A是数组, 那调用后, 每个A的元素都是一个随机数.
数组操作
- 数组置零:
- A=0
- 数组切片置零:
- 某列置零A(:, 2)=0
- 某行置零A(2,:)=0
- 奇数行置零A(1::2,:)=0
- 奇数行偶数列置零: A(1::2,2::2)=0
- 数组Copy:
- 整体Copy:A=B
- 部分切片复制:A=C(1:10:2, :)
- 数组运算
- 加法:A=B+C
- 乘法:A=B*C
- 除法:A=B/C
- 矩阵乘法:A=matmul(B,C)
- IO读写:
- write(11) A
- read(11) A
- write(11) A(:,2)
- read(11) A(:,2)
说明
-
如果用过Matlab的话, 就比较容易理解Fortran中多维数组的语法, 两者不同点是, 对切片语法的定义Matlab是 start_index:step:end_index, Fortran是start_index:end_index:step
-
程序中应该尽可能的使用数组整体操作, 代码行数会较少, 性能上也可能会带来好处
-
Fortran中很多内置函数都能直接用在数组上
代码阅读、调试、编译、链接
-
implicit none.
Fortran默认情况下变量是不需要声明, 调试程序的时候多数人会遇见因为拼写错误, 获得一些异常结果, 将变量声明打开, 编译时会检查出这类错误. - namelist.
名字列表在调试期间和测试期间比较有用, 它可以将多个变量捆成一组, 进行集中输出和输入. 有些情况下可以用namelist代替命令行参数传入:将需要的参数以namelist的方式传入程序, 避免了繁琐的参数解析过程!namelist确实很方便, 比如对于数组A(3,2), 可以使用类似
A(1:3,2) = 5,3,4
A(1:3,1) = 2,0,9
或者A = 2,0,9,5,3,4
这样的方式来输入namelist在调试和测试中使用是非常不错的!举个例子:
program main implicit none integer ::a real(4)::x real(8)::y namelist /debug1/ a namelist /debug2/ x, y namelist /debug3/ a, x, y a=100 x=1.1111_4 y=2.2222_8 write(*, nml=debug1) write(*, nml=debug2) write(*, nml=debug3) end program main
-
Preprocess.
Fortran支持类C的预处理, fpp, 在调试程序时较有用处. -
Emacs.
Emacs内置Fortran语言支持, 编码和调试都可在Emacs下进行. - understand for fortran 是阅读代码不错的工具软件, 类似SourceInsight
understand for fortran是要钱的, 破解找起来比较烦. 建议使用Doxygen生成HTML文档, 所有的调用/被调用, 引用关系全部能够生成. 在浏览器里面点点就知道程序的结构了.
- linux工具:gdb, ddd, nm, ldd, od.
gdb+ddd通用调试工具; nm可以看目标文件的符号等信息, 可以帮助诊断连接中的问题, 比如找不到符号; ldd能用来看程序需要的共享库信息; od能直接看二进制文件内容.
文件IO
-
创建临时文件: open(unit=11, status=’scratch’,…)
- 查看IO操作状态
- IO操作返回值:open(unit=11, iostat=stat, …), 检查整数stat的值, 可以确定IO操作的成功与否!
- EOF、ERR
- 说明: Fortran的IO操作是可以返回状态的, 以检查操作成功与否, 在健壮性要求较高的程序中, 一般都应该检查这些值
- 流文件(二进制文件)
- 打开二进制文件:open(unit=11, access=’stream’,…)
- 将数组写入二进制打开的文件:write(unit=11) A
- 从二进制文件读数组: read(unit=11) A
- 说明. 传统的Fortran文件读写都是基于记录的, 不管是有格式的还是无格式的, 新标准支持二进制读写, 和C语言很类似
- 异步IO
- 异步打开文件:open(unit=11, asynchronous=’yes’,…)
- 异步写:write(unit=11, id=handle, asynchronous=’yes’,….) ….
- 异步读:read(unit=11, id=handle, asynchronous=’yes’,….) …..
- 等待异步IO完成:wait(unit=11, id=handle)
- 说明: 如果文件中有大块数据的IO操作, 用异步IO能在一定程度上使计算和磁盘IO重叠进行, 提高效率!
- 第三方库
- MPI-IO:MPI-IO提供一套类似C语言的IO函数, 如果在MPI_File_open()中指定comm是MPI_COMM_SELF, 就相当于操作本地文件.
- HDF5:
- NetCDF、PnetCDF:
- 说明: HDF5、NetCDF、PnetCDF都提供结构化的存储模式, 而且都提供并行IO操作接口.
浮点数运算(提纲)
- 浮点运算异常
- 除零
- 向下溢出
- 向上溢出
- 捕获浮点运算异常
- 查询浮点数信息:Fortran内置函数
- IEEE浮点数标准
- IEEE浮点数异常控制
- IEEE_FEATURES
- IEEE_EXCEPTIONS
- IEEE_ARITHMETIC
- 浮点数运算
- 不满足结合律, 运算次序非常重要:(A+B)+C .NEQ. A+(B+C)
- 舍入误差
- 差不多的两数相减
- 避免进行等于比较: A .EQ. B 5.数组求和:使用内置函数SUM
- HYPOT计算: SQRT(X2+Y2)
- 二次方程求根
- 三角形面积:海伦公式
- 对数函数:Ln(1+X)
- X2-Y2
- 级数求和
- 特殊情况特殊对待
- 使用扩展精度
- 带符号零
- 浮点运算的Guard Bits
- 相对误差
- 绝对误差
- 特殊数: -NaN, +NaN, -Infinity, +Infinity
- 极大极小值: HUGE, TINY, EPSILON
参考
- David, Goldberg, What Every Computer Scientist Should Know About Floating-Point Arithmetic
- D.Knuth
- IEEE 754
- The Fortran 2003 Handbook
- Fortran 90,95 Explained