我的程序中存在问题。使用掩码调用的内部函数sum会导致可疑结果:当我执行平均值时,我从数组边界中获取一个值。我怀疑这与舍入错误有关。我正在使用大型阵列,并且舍入误差会导致较大的偏差(与40,000个元素的预期值相比差异大约为40%)。
下面是重现它的最小示例,以及相关的输出。
program main
implicit none
integer :: nelem
real, allocatable, dimension(:) :: real_array
logical, allocatable, dimension(:) :: log_array
! init
nelem=40000
allocate(real_array(nelem))
allocate(log_array(nelem))
real_array=0.
log_array=.true.
! Fill arrays
real_array=1./2000.
log_array = real_array.le.(0.5)
! Test
print *, ' test : ', &
count(log_array)+sum(real_array, mask=log_array), &
sum(1.+real_array,mask=log_array)
end program main
输出是:
test : 40019.9961 40011.9961
理论结果是40020。
运行GNU Fortran(GCC)4.9.0
答案 0 :(得分:1)
您正在使用单精度数组。计算机基本上将实数存储为2的幂的扩展。这适用于2和4和8之类的数字,依此类推,可以很容易地写成具有整数系数的2的整数幂但对于某些实数不太好(如1.d0 / 2000.d0)。
单精度
real, allocatable, dimension(:)
分配4个字节。这将给你8位数的精度。这就是你观察到的。第二笔金额
sum(1.+real_array,mask=log_array)
只有四位数的精度,但是,你正在添加1.0和小1000倍的东西。这使得它有效地缩小到四位数(这是你在第二种情况下观察到的)。
你可以通过声明所有双精度(也就是精确到16位精度的8字节变量)来改进,而不是1.0,你必须写1.d0,或者你添加一个编译器标志,如-fdefault-real-8 -fdefault双-8。
如果您的操作过程中的舍入错误累积太多,我建议您重新考虑做事的顺序。添加范围大不相同的变量将显着降低您的精度。
如果这不是一个选项,双精度是不够的,我可以指出你的四倍精度
但我个人没有使用它,因为这通常是通过软件解决的 层会产生巨大的性能损失。
编辑:尝试双精度:
改变:
double precision, allocatable, dimension(:) :: real_array
保留其余部分并使用上述编译器选项进行编译。我获得了
test : 40020.000000000000 40019.999999987354
第一个结果很好,第二个是12位精度(原来是16位数加上通过添加1.0和1.0 / 2000.0而丢失的四位数字),这也是你所期望的。