2016-05-23 82 views
0

我在我的程序中有一個問題。用掩碼調用的內部函數總和會導致可疑的結果:當我執行平均值時,我從數組邊界中獲得一個值。我懷疑這與舍入錯誤有關。我正在處理大數組,並且舍入誤差會導致較大的偏差(與40,000個元素大小的預期值相比,差異約爲40%)。問題與sum(array,mask = ...)

下面是重現它的最小示例以及相關的輸出。

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

可能重複[浮點數學是否被破壞?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) –

回答

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倍的東西。將其縮小到有效四位數字(這是您在第二種情況下觀察到的情況)。

你可以通過聲明所有的雙精度(也就是8個字節的變量,精度爲16位)來改進它,而不是1.0,你將不得不編寫1.d0,或者添加一個編譯器標誌,如-fdefault-real- 8 -fdefault-double-8。

如果您的運算過程中您的舍入誤差累積得太多,我建議重新考慮處理次序。添加極其不同範圍的變量會顯着降低精度。

如果這不是一種選擇,雙精度是不夠的,我可以指出你四精度

quad precision in gfortran

但我個人沒有使用它,因爲這通常是通過軟件 層解決預計會有巨大的性能損失。

編輯:雙精度嘗試:

變化:

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

保持休息,並與所提到的編譯器選項編譯。我獲得

test : 40020.000000000000  40019.999999987354 

的第一個結果是好的,第二個是12位精度(原來16個位數加四個數字加上1.0和1.0/2000.0丟失)這又是你所期望的。

+0

雖然我不能將所有真正的數組轉換爲雙精度,本地切換到雙精度工作:真正(sum(dble(myarray),mask = mymask))。這是一個醜陋的技巧,但工作之一... Thx – user1824346