计算圆周率的小程序_高精度算法练习

  1. 使用的公式:

PI=2+1/3*(2+2/5*(2+3/7*(2+…(2+k/(k+1)*2)))….)))

化简为迭代情况:

  1. 为了保证效率,高精度运算采用 10000 进制运算。

  2. 由于此高精度问题,不能乘以/除以一个比较大的数(乘法的结果不能大于 31bit,除法的除数不能大于 31bit,乘法相对容易解决,除法就比较麻烦),所以不能计算无限位的 PI。

  3. 关于本程序处理极限的讨论

迭代极限次数:107382; 最高精度:小数点后第 32327 位; 具体情况如下:

…861893818959054203… (PI的值)
…       |              (竖线标记了小数点后第32327位)
…861893815             (运算结果)

其实,这个程序的效率比较低了,即使没有处理极限,算下去也没有什么意义 所以这里不进行高精度算法修改了,要算更大精度的,最好去换 PI 的算法了。

  1. 迭代次数大概是要求精度位数的 3.4 倍。

  2. 细节:运算先做乘法,在做除法会造成精度严重损失(不论迭代多少次, 都只有 750 多位精度),猜想是余数处理问题,具体细节未深究,所以修改后的, 就先做除法,在做乘法。

  3. 确定本程序精度参考了 SuperPI 的运算结果。

#include <stdio.h>
#define T 330/*迭代次数,最大:107382*/
#define N 25
/*数组保存小数部分的长度,精度的四分之一,最大:32327/4=8081.75,也就是8082*/
/*试验表明,迭代330次算的PI小数点后的100位已经是精确的了*/

int main(){
  int PI[N+1]={0},i,j,k,t;
  PI[0]=2;/*初始化*/
  for(i=T;i;i--){
  /* PI/(2*i+1) */
    t=0;/*除法了,这里保存余数,初始当然是0*/
    k=2*i+1;/*要除以的数,见PI的公式*/
    for(j=0;j<=N;j++){
      PI[j]+=t*10000;/*被除数是上一位的余数乘以进制再加上这一位上的数*/
      t=PI[j]%k;/*保存这次运算的余数,供下次使用*/
      PI[j]/=k;/*这次运算的结果*/
    }

  /* PI*i */
    t=0;/*保存乘法进位,初始当然是0*/
    for(j=N;j>=0;j--){
      t+=PI[j]*i;/*上次的进位加上这次乘得的结果就是这次的值*/
      //if(t<0){printf("Overflown"); return 0;}/*运算超过精度限制了*/
      PI[j]=t%10000;/*本位只保存余数*/
      t/=10000;/*这里就是保存进位*/
    }
    //if(t)printf("Unexpected!n");
    /*如果成立了,也就是整数部分的结果大于10000了,明显是错误的。*/

  /* PI+2 */
    PI[0]+=2;
    /*加上2,由于整数2小数点后面是零,所以小数点后面的不参与运算了(不是废话么?)。*/
    //printf("%d %d.",i,PI[0]);int ii;
    //for(ii=1;ii<=N;ii++)printf("%.4d",PI[ii]);
    //printf("n");getchar(); /*一次次观察结果*/
  }
  printf("%d.n",PI[0]);/*第一位为整数部分*/
  for(i=1;i<=N;i++){
    printf("%.4d",PI[i]);/*%.4d输出结果占四位,而且,数值小要补零*/
    if(i%5==0)printf("n");/* 4*5=20 每20位输出作一行 */
  }
  printf("n");
  //getchar();
  return 0;
}