[英]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中聲明k
為integer(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.