问题
问题如图,高精度计算pi值用泰勒展开式计算。
参考
参考了大佬的代码后加了原理以及注释;大佬原文
https://blog.csdn.net/zhao2018/article/details/79929881?utm_source=app&app_version=4.6.0
原理
1.由于计算的精度会达到很多位数,而浮点型数据最多位数32或64位,无法满足需求,则只能考虑自己建立一个元素组来计算,其中有整数部分,还有很多位数的小数部分。暂时考虑用链表计算,每个节点存储一位数的值。
2.计算时观察泰勒展开式发现表达式是将n个具有递推关系的式子Rn相加,而Rn与Rn-1关系式中有乘法除法,则考虑设计加法,乘法,除法函数。
3.由于多位数计算加法和乘法需从最低位开始计算,而单向链表定位到最低位比较困难,则考虑双向循环链表,可使p-pre从而指向最后一个结点—表示最低位的数的值。
4.分别写出各运算的算法—需考虑进位,补数等竖式计算原则。
5.循环内进行运算,运算结束后根据n的值来输出链表的前面多少个结点,从而得到最终pi的值。
代码
代码以及相关注释如下:
#include <stdio.h>
#include <stdlib.h>
#define MAX 1000
typedef struct Node
{
int data;//每个结点一个data,代表每个位数上的值
struct Node* pre;
struct Node* next;
} LNode, * LinkList;
void Create(LinkList L);
void Sum(LinkList a, LinkList b);
void Multiply(LinkList a, int k);
void Divide(LinkList a, int k);
int main()
{
int n, i, top, bottom, s = 1;
printf("请输入n值(即精确到小数点后多少位)");
scanf_s("%d", &n);
LinkList sum, R;
sum = (LinkList)malloc(sizeof(LNode));
R = (LinkList)malloc(sizeof(LNode));
Create(sum);
Create(R);
sum->next->data = 3;//之所以取3是因为pi是6分之pi的6倍,而R1同理也应该乘以6,才能求出pi而不是6分之pi
R->next->data = 3;
while (s < 2000)
{
top = (2 * s - 1) * (2 * s - 1);//top是Rn与Rn-1关系式当中的分子
bottom = 8 * s * (2 * s + 1);//bottom是Rn与Rn-1关系式当中的分母
Multiply(R, top);//每一位与top相乘
Divide(R, bottom);//每一位与top相除
Sum(R, sum);//将Rn与Rn-1相加
s++;
}//计算1999个式子相加后得到的值即为
if (n == 0)
{
printf("3\n");
}//精度取整
else
{
sum = sum->next;
printf("%d.", sum->data);//整数部分后加小数点
for (i = 0; i < n; i++)
{
printf("%d", sum->next->data);
sum = sum->next;
}//实际上以及计算出sum链表的每一位,这里n为多少,输出多少为小数点后的即取多少位精确值
}
printf("\n");
return 0;
}
void Create(LinkList L)
{
LinkList p = L, q;
int i;
L->next = L->pre = L;
for (i = 0; i < MAX; i++)
{
q = (LinkList)malloc(sizeof(LNode));
q->data = 0;
p->next = q;
q->pre = p;
q->next = L;
L->pre = q;
p = q;
}//MAX在这里确定了最终链表是1000个元素
}//建立了一个双向循环链表,里面的结点元素全部为0-》循环链表可以使之第一次就pre从而指向尾部-》位数最小的那位-》符合多位数的运算
void Sum(LinkList a, LinkList b)
{
LinkList p = a->pre, q = b->pre;
int n;
while (q != b)
{
n = q->data + p->data;//低位相加
q->data = n % 10;//取余得低位的结果
q->pre->data += n / 10;//高位进位
q = q->pre;
p = p->pre;
}//从最低位开始加起直到走完一个循环
}//每一位数都相加,考虑进位
void Multiply(LinkList a, int n)
{
LinkList p = a->pre;
int x, y = 0;
for (; p != a; p = p->pre)
{
x = (p->data) * n + y;//y是比该位低的那一位做乘法运算时多出来的需要加到高位的,初始化时为0
y = x / 10;//作为下一次加到高位的数
p->data = x % 10;//本位的结果
}
x = (p->data) * n + y;
y = x / 10;
p->data = x % 10;
}//每一位从低位到高位依次与top相乘
void Divide(LinkList a, int n)
{
LinkList p = a->next;
int x, y = 0;
for (; p != a; p = p->next)
{
x = p->data + y * 10;//上一位到这一位补充的y乘以10,y初始化时为0
p->data = x / n;//本位结果
y = x % n;//除法得到的余数进入比本位低的那位乘以10加上去
}
}//每一位从高位到低位依次与bottom相除