一、使用线性表突破程序设计语言内置变量的数值和有效数字范围的限制,为了实现高精度圆周率的计算,先根据数学公式进行对PI高精度运算,如图1-1。根据这个数学公式 π2 = 0nn!2n+1‼ 即 Rn+1=Rn*n2n+1,R1=1,sum= π=2* n=1∞Rn 来计算PI值。首先先建立双链表,再对其初始化。根据其特性,实现了对大数的加法,乘法和除法,以此来完成对PI的高精度计算如图1-1,最后将PI的值储存到文件中如图1-2。
#include <stdio.h>
#include <stdlib.h>
// 定义双链表节点结构体
typedef struct list
{
int data; // 节点数据域
struct list *next; // 指向下一个节点的指针
struct list *pre; // 指向前一个节点的指针
}list;
// 初始化双链表,返回链表头节点
list* Initlist()
{
list *head;
head = (list*)malloc(sizeof(list)); // 为头节点分配内存
if (!head)
exit(EXIT_FAILURE); // 如果内存分配失败,则退出程序
else
head->next = head->pre = head; // 初始化头节点的next和pre指针指向自己
return head;
}
// 创建指定长度的双链表,返回链表头节点
list *Creatlist(list *head)
{
list *p = head;
for (int i = 0; i < 1000; i++) // 循环创建1000个节点
{
list *q = (list*)malloc(sizeof(list));
if (!q)
exit(EXIT_FAILURE); // 如果内存分配失败,则退出程序
else
{
q->data = 0; // 将新节点q的数据域设置为0
q->pre = p; // 将新节点q的前置指针指向当前节点p
p->next = q; // 将当前节点p的下一个节点指针指向新节点q
q->next = head; // 将新节点q的下一个节点指针指向链表的头节点head,形成一个环形链表
head->pre = q; // 将头节点head的前置指针指向新节点q,完成环形链表的构建
p = p->next; // 将当前节点p移动到下一个节点,为下一次循环做准备
}
}
return head;
}
void Save(int n) //存储PI
{
FILE *fp = fopen("E:\\C语言的软件\\数据结构与算法\\1\\PI的存储.txt","w+");
fprintf(fp,"%d",n);
fclose(fp);
}
int main()
{
int n;
printf("请输入PI要精确到的位数\n");
scanf("%d", &n); // 输入需要的小数位数
list *number, *sum;
list *p, *q, *x;
number = Initlist(); // 初始化number链表
sum = Initlist(); // 初始化sum链表
number = Creatlist(number); // 创建number链表
sum = Creatlist(sum); // 创建sum链表
number->next->data = 2; // number链表的第二位储存2,即2*R(1)=2
sum->next->data = 2; // sum链表的第二位也储存2
int a = 0, b = 0; // 分别是用来暂时存储进位和余数
for (int i = 1; i < 2000; i++) // 循环两千次,用于计算π的近似值
{
p = number->pre; // 开始大数乘法时从number链表的尾部开始
while (p != number) //大于10则把10位的数字给b,个位数字放入data域中。
{ //乘法运算
a = p->data * i + b; // 计算当前位乘以i加上之前的进位
p->data = a % 10; // 取个位数放入当前节点的data域
b = a / 10; // 计算进位
p = p->pre; // 移动到前一个节点
}
b = 0; // 清空b,为除法做准备
p = number->next; // 开始大数除法时从number链表的前部开始
while (p != number) //若计算出的数字为自然数,则直接放入data域;若等于0,或为小数,则要计算余数并给b。
{ //除法运算
a = p->data + b * 10; // 计算当前位加上之前的余数乘以10
p->data = a / (2 * i + 1); // 计算当前位的商,并放入当前节点的data域
b = a % (2 * i + 1); // 计算余数
p = p->next; // 移动到下一个节点
}
b = 0; // 清零b
p = number->pre; // 开始大数加法时从number链表的尾部开始
q = sum->pre; // 从sum链表的尾部开始
while (p != number) //大于10进位,并储存个位数,进位数字赋给b。
{ // 加法计算
a = p->data + q->data + b; // 计算当前位的和加上之前的进位
q->data = a % 10; // 取个位数放入sum链表的当前节点的data域
b = a / 10; // 计算进位
p = p->pre; // 移动到number链表的前一个节点
q = q->pre; // 移动到sum链表的前一个节点
}
}
printf("3."); // 输出π的前缀"3."
x = sum->next->next; // 从sum链表的第三个节点开始输出,即输出π的小数部分
FILE *fp = fopen("E:\\C语言的软件\\数据结构与算法\\1\\PI的存储.txt","w+");
fprintf(fp,"3.");
for (int i = 0; i < n; i++) // 输出n位小数
{
fprintf(fp,"%d",x->data); //存储PI
printf("%d", x->data); // 输出当前节点的data域的值
x = x->next; // 移动到下一个节点
}
fclose(fp);
return 0;
}