sum(array,mask = ...)的问题

时间:2016-05-23 15:07:10

标签: arrays sum fortran gfortran

我的程序中存在问题。使用掩码调用的内部函数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

1 个答案:

答案 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。

如果您的操作过程中的舍入错误累积太多,我建议您重新考虑做事的顺序。添加范围大不相同的变量将显着降低您的精度。

如果这不是一个选项,双精度是不够的,我可以指出你的四倍精度

quad precision in gfortran

但我个人没有使用它,因为这通常是通过软件解决的 层会产生巨大的性能损失。

编辑:尝试双精度:

改变:

double precision, allocatable, dimension(:) :: real_array

保留其余部分并使用上述编译器选项进行编译。我获得了

 test :    40020.000000000000        40019.999999987354   

第一个结果很好,第二个是12位精度(原来是16位数加上通过添加1.0和1.0 / 2000.0而丢失的四位数字),这也是你所期望的。