- 使用的公式:
PI=2+1/3*(2+2/5*(2+3/7*(2+…(2+k/(k+1)*2)))….)))
化简为迭代情况:
-
初始时:PI=2
-
循环迭代:
- PI=PIi/(2i+1)+2
- 其中 i 位迭代变量,从一个很大的数到 1,只要这个数足够大,
- 那么 PI 的精度就越高。
公式来源于互联网,原始网址已经丢失,对此感到抱歉。
-
为了保证效率,高精度运算采用 10000 进制运算。
-
由于此高精度问题,不能乘以/除以一个比较大的数(乘法的结果不能大于 31bit,除法的除数不能大于 31bit,乘法相对容易解决,除法就比较麻烦),所以不能计算无限位的 PI。
-
关于本程序处理极限的讨论
迭代极限次数:107382; 最高精度:小数点后第 32327 位; 具体情况如下:
…861893818959054203… (PI的值)
… | (竖线标记了小数点后第32327位)
…861893815 (运算结果)
其实,这个程序的效率比较低了,即使没有处理极限,算下去也没有什么意义 所以这里不进行高精度算法修改了,要算更大精度的,最好去换 PI 的算法了。
-
迭代次数大概是要求精度位数的 3.4 倍。
-
细节:运算先做乘法,在做除法会造成精度严重损失(不论迭代多少次, 都只有 750 多位精度),猜想是余数处理问题,具体细节未深究,所以修改后的, 就先做除法,在做乘法。
-
确定本程序精度参考了 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;
}