简体   繁体   English

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

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

I'm trying to compare C and Fortran code for performance. 我正在尝试比较C和Fortran代码的性能。 For calculating pi using Leibniz's series , I've got the following Fortran code 为了使用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

and a nearly identical C code: 和几乎相同的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;
} 

I compile both with GNU compilers and just a -O2 option. 我用GNU编译器和只有-O2选项编译。 Edit: 64-bit. 编辑:64位。

The Fortran code runs happily up to the full double precision, calculating the first 15 digits of pi in a couple of seconds on my machine. Fortran代码快速运行到完全双精度,在我的机器上计算几秒内pi的前15位数。 The C code performs even slightly faster than Fortran up to 8 decimal places, converging to the same digits in the same number of iterations; C代码的执行速度甚至比Fortran快8位小数,在相同的迭代次数内收敛到相同的位数; however, with precision=9 the Fortran code converges to 3.141592653 in 2.27s/1581043254 iterations, while the C code takes 12.9s/9858058108 iterations (~6x) and the last digit is off by 1. With higher precision, the time for Fortran is of the same order while C takes ~2 minutes to compute the first 11 digits of pi. 但是,如果precision=9则Fortran代码在2.27s / 1581043254迭代中收敛到3.141592653,而C代码需要12.9s / 9858058108次迭代(~6x),最后一位数则关闭1.精度更高,Fortran的时间更长具有相同的顺序,而C需要约2分钟来计算pi的前11位数。

What could be the reason for the discrepancy and how do I avoid whatever is slowing down the C code? 可能是造成这种差异的原因是什么?如何避免任何减慢C代码的速度?

Edit: I did as @pmg suggested and changed the loop in the C code, making the convergence monotonic: 编辑:我做了@pmg建议并更改了C代码中的循环,使收敛单调:

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

While this speeds up convergence somewhat at lower precision, it actually makes the C program essentially hang even at precision=8 now (takes more than 3 minutes to compute). 虽然这会在较低的精度下加速收敛,但它实际上使C程序在precision=8基本上挂起(计算时间超过3分钟)。

Edit 2: Since computing at precision>8 results in integer overflow, it seems that the correct way was to declare k was as integer(8) :: k in Fortran and unsigned long in C. With this modification, the Fortran code now performs almost exactly as the C code for 10/11 digits of pi and seems to 'hang' at higher precision. 编辑2:由于precision>8计算导致整数溢出,似乎正确的方法是在Fortran中声明kinteger(8) :: k ,在C中声明为unsigned long 。通过此修改,Fortran代码现在执行几乎与pi的10/11数字的C代码完全一样,并且似乎以更高的精度“挂起”。

How come, then, that using an essentially incorrect method still produced the correct result before, and took the same amount of time to calculate whether it was 10 or 15 digits of pi? 那么,为什么使用一个本质上不正确的方法之前仍然产生了正确的结果,并花了相同的时间来计算它是10或15位pi? Just for fun, it took 1611454902 iterations to 'converge' to 3.14159265358979, which happens to be exactly pi to 14 decimal places. 只是为了好玩,它需要1611454902次迭代才能“收敛”到3.14159265358979,恰好是pi到14位小数。

Your Fortran code is incorrect. 您的Fortran代码不正确。

You are likely using a default integer to be 32-bit and using HUGE(k) you will see that the maximum integer value k can take is 2147483647. In this case you will have integer overflow happening with the iteration count and (before then) with its evaluation in real(2*k+1,dp) . 您可能使用默认整数为32位并使用HUGE(k)您将看到可以采用的最大整数值k是2147483647.在这种情况下,您将发生整数溢出与迭代计数和(之前)以其real(2*k+1,dp)评估real(2*k+1,dp)

Much as you use selected_real_kind to find a real kind fitting your requirements, you use should selected_int_kind to find a suitable integer kind. 就像使用selected_real_kind找到符合您要求的真实类型一样,您应该使用selected_int_kind来查找合适的整数种类。 If we trust the C version, then the iteration count could reach such a large number that k should have kind selected_int_kind(11) . 如果我们信任C版本,那么迭代计数可以达到如此大的数量, k应该具有kind_int_kind selected_int_kind(11)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM