Fortran中存储的浮点加法结果与临时变量比较的异常问题
Fortran浮点数
==运算符的费解行为解析 我完全懂你这种困惑的感觉——明明知道浮点数有精度局限,像0.1 + 0.2 == 0.3这种表达式大概率不成立,但总觉得一些看起来“绝对相等”的极简程序,怎么着都该输出.true.对吧?
先给你拆解几种可能的情况:
1. 直接赋值同精度字面量:肯定会相等
如果你的代码是类似下面这种,直接给同精度的浮点变量赋值完全一样的字面量,那==返回.true.是板上钉钉的:
program main integer, parameter :: dp = kind(0d0) real(kind=dp) :: a, b a = 4.0_dp b = 4.0_dp print *, a == b ! 输出 .true. end program main
这时候两个变量存储的是完全相同的二进制浮点表示,比较结果必然为真。
2. 不同计算路径得到“数学相等”的值:可能不相等
哪怕两个数在数学上完全相等,但只要是通过不同的浮点运算路径得到的,就可能因为舍入误差导致二进制表示有细微差别,这时候==就会返回.false.。举个极简的例子:
program main integer, parameter :: dp = kind(0d0) real(kind=dp) :: a, b a = 1.0_dp / 3.0_dp b = 1.0_dp - 2.0_dp / 3.0_dp print *, a == b ! 很大概率输出 .false. print *, abs(a - b) < 1e-15_dp ! 这个会输出 .true. end program main
数学上1/3和1-2/3完全相等,但浮点计算中,除法和减法的舍入操作会让两者的二进制存储产生极小的差异,直接用==比较就会失败。
3. 不同精度浮点数的比较:注意转换损失
如果你的代码里混用了不同精度的浮点类型(比如单精度real和双精度real(dp)),哪怕字面量看起来一样,类型转换过程中也可能出现精度损失,导致==不成立。比如:
program main integer, parameter :: dp = kind(0d0) real :: a ! 单精度 real(kind=dp) :: b ! 双精度 a = 0.1 b = 0.1_dp print *, a == b ! 输出 .false. end program main
单精度的0.1和双精度的0.1二进制表示本身就不一样,比较时单精度值会被转换为双精度,但转换后的结果和原生双精度的0.1还是有差异,所以==不成立。
总结
如果你的极简程序没有输出预期的.true.,大概率是上面后两种情况之一——要么是变量通过不同计算路径生成,要么是混用了不同精度的浮点类型。哪怕看起来再“简单”,只要涉及浮点运算的舍入,就不能想当然地用==比较,还是得回到abs(x - y) < tolerance的标准做法。
内容的提问来源于stack exchange,提问作者mjacobse
相关产品推荐
相关产品推荐

