簡體   English   中英

用C和Fortran中的Leibniz系列計算Pi

[英]Calculating Pi with Leibniz's series in C and Fortran

我正在嘗試比較C和Fortran代碼的性能。 為了使用Leibniz系列計算pi,我得到了以下Fortran代碼

program pi_leibniz
implicit none

    integer, parameter :: dp=selected_real_kind(15,307)
    integer :: k=0, precision=9
    real(dp), parameter :: correct = 0.7853981633974483d0, eps = epsilon(real(1,dp)) 
    real(dp) :: sum = 0.0, delta
    character(8) :: fmt
    logical, parameter :: explicit = .false.
    real :: start, finish

    delta = 10.**(-precision-1)*0.25
    if (delta<eps) then
        delta=eps
        precision=14
        print *, "Precision specified too high, reverting to double precision (14 digits)"
    endif

    write(fmt,'(A,I0,A,I0,A)') '(f',precision+2,'.',precision,')'

    call cpu_time(start)

    do
        sum = sum + real((-1)**k,dp)/real(2*k+1,dp)
        k = k+1
        if (abs(sum-correct)<delta) exit        
        if (explicit) print fmt, 4.*sum 
    enddo

    call cpu_time(finish)

    print fmt, 4.*sum
    print '(A,I0,A,I0,A)', "converged in ", k, " iterations with ", precision, " digits precision"
    print '(g0,a)', finish-start," s"

end program pi_leibniz

和幾乎相同的C代碼:

#include <stdio.h>
#include <time.h>
#include <float.h>
#include <math.h>


int main(void){
    int precision=9;
    size_t k=0;
    const double correct=0.7853981633974483;
    double sum=0.0, delta = 0.25*pow(10.0,-(precision+1));
    clock_t start,finish;

    double sgn = 1.0;

    if (delta < DBL_EPSILON){
        delta = DBL_EPSILON;
        precision = 14;
        printf("Precision specified too high, reverting to double precision (14 digits)\n");
    }

    start = clock();

    for(k=0; fabs(sum-correct) >= delta; k++, sgn=-sgn)
        sum += sgn/(2*k+1);

    finish = clock();

    printf("%.*f\n",precision,4*sum);
    printf("converged in %zu iterations with %d digits precision\n",k,precision);
    printf("%f s\n",(finish-start)/(double)CLOCKS_PER_SEC);

    return 0;
} 

我用GNU編譯器和只有-O2選項編譯。 編輯:64位。

Fortran代碼快速運行到完全雙精度,在我的機器上計算幾秒內pi的前15位數。 C代碼的執行速度甚至比Fortran快8位小數,在相同的迭代次數內收斂到相同的位數; 但是,如果precision=9則Fortran代碼在2.27s / 1581043254迭代中收斂到3.141592653,而C代碼需要12.9s / 9858058108次迭代(~6x),最后一位數則關閉1.精度更高,Fortran的時間更長具有相同的順序,而C需要約2分鍾來計算pi的前11位數。

可能是造成這種差異的原因是什么?如何避免任何減慢C代碼的速度?

編輯:我做了@pmg建議並更改了C代碼中的循環,使收斂單調:

for(k=0; fabs(sum-correct) > delta; k+=2)
    sum += 1.0/(2*k+1) - 1.0/(2*k+3);

雖然這會在較低的精度下加速收斂,但它實際上使C程序在precision=8基本上掛起(計算時間超過3分鍾)。

編輯2:由於precision>8計算導致整數溢出,似乎正確的方法是在Fortran中聲明kinteger(8) :: k ,在C中聲明為unsigned long 。通過此修改,Fortran代碼現在執行幾乎與pi的10/11數字的C代碼完全一樣,並且似乎以更高的精度“掛起”。

那么,為什么使用一個本質上不正確的方法之前仍然產生了正確的結果,並花了相同的時間來計算它是10或15位pi? 只是為了好玩,它需要1611454902次迭代才能“收斂”到3.14159265358979,恰好是pi到14位小數。

您的Fortran代碼不正確。

您可能使用默認整數為32位並使用HUGE(k)您將看到可以采用的最大整數值k是2147483647.在這種情況下,您將發生整數溢出與迭代計數和(之前)以其real(2*k+1,dp)評估real(2*k+1,dp)

就像使用selected_real_kind找到符合您要求的真實類型一樣,您應該使用selected_int_kind來查找合適的整數種類。 如果我們信任C版本,那么迭代計數可以達到如此大的數量, k應該具有kind_int_kind selected_int_kind(11)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM