目录
高精度算法(High Accuracy Algorithm)是处理大数字的数学计算方法。在一般的科学计算中,会经常算到小数点后几百位或者更多,当然也可能是几千亿几百亿的大数字。一般这类数字我们统称为高精度数,高精度算法是用计算机对于超大数据的一种模拟加,减,乘,除,乘方,阶乘,开方等运算。对于非常庞大的数字无法在计算机中正常存储,于是,将这个数字拆开,拆成一位一位的,或者是四位四位的存储到一个数组中, 用一个数组去表示一个数字,这样这个数字就被称为是高精度数。高精度算法就是能处理高精度数各种运算的算法,但又因其特殊性,故从普通数的算法中分离,自成一家。
一、高精度加法。(不考虑负数)
因为任何数据类型都有一定的表示范围。而当两个被加数很大时,在c语言中也没有任何一个数据类型可以储存该数据。如果我们用数组A、B分别存储加数和被加数,用数组C存储结果,就可以用下图来描述。
有几个需要注意的点。
1.使用字符串来储存数字,所以它是倒序储存的,我们计算时要从后面往前计算。
2.像这种倒序存放的数据的计算结果的输出要小心前导零的输出,所以我们也要消掉0再输出。
3.计算时要注意进位,每一次的计算下一位要先加上上一次计算的进位。
#include<stdio.h>
#include<string.h>
#define MAX 100010
int a[MAX] = { 0 };
int b[MAX] = { 0 };
int c[MAX] = { 0 };
char ch[MAX];
int fun(char str[], int a[]) {//转换为数字
char* p = ch;
for (int i = strlen(ch) - 1; i >= 0; i--) {
a[i] = (*p) - '0';
p++;
}
return strlen(ch);//计算字符长度
}
int main() {
int t = 0;
int num1, num2;
scanf("%s", &ch);
num1 = fun(ch, a);//将字符数字转换为可计算的数字,顺便记录它的字符长度
scanf("%s", &ch);
num2 = fun(ch, b);
if (num1 < num2) {//将两者的字符长度进行比较,用字符长的确保历遍两者的储存的数据
int temp = num1;
num1 = num2;
num2 = temp;
}
int num;//初次计算当前位数两者结果
for (int i = 0, k = num1; i <= num1; k--, i++) {//使用k来记录 计算结果 C 数组 长度还可以逆置计算结果,方便输出
num = a[i] + b[i];//i为较长串,历遍两个数组
if (t != 0) {//如果进位不为0说明,需要加上进位,再复零
num += t; t = 0;
}
if (num < 10)//不需要进位
c[k] = num;
else
{
while (num >= 10) {//大于十则需要进位
t++;
num -= 10;
}
c[k] = num;//加上当前小于10时的最终数字
}
}
if (c[0] == 0)//防止前导零的出现,可以通过t来控制,要是没有进入该条件,t则为0
t = 1;
for (int i = t; i <= num1; i++)
printf("%d", c[i]);
return 0;
}
二、高精度数减法
1、和高精度加法相比,减法在差为负数时处理的细节更多一点:当被减数小于减数时,差为负数,差的绝对值是减数减去被减数;所以就要保持绝对值较大数作被减数,最后在结果前加上负号。计算就使用小学竖式计算即可。
2.运算:先判断当前位数的相减是否为负数,是否需要进行借位,借位后要在被借数减1。
3.也是需要判断前导零的情况之后,再将其输出。
#include<stdio.h>
#include<string.h>
#define MAX 100010
int fun(char str[], int str2[])//进行数据转换
{
char* p = str;
int num = strlen(str) - 1;
for (int i = num, j = 0; j <= num; i--, j++)
str2[i] = p[j] - '0';
return num;
}
char str1[MAX], str2[MAX];//使用两个个字符数组储存,用于应对出现负数情况
int a[MAX], b[MAX], c[MAX];
int main() {
scanf("%s", str1);
scanf("%s", str2);
int num1 = strlen(str1);//转换并返回字符长度
int num2 = strlen(str2);
if (num1 < num2 || num1 == num2 && strcmp(str1, str2) < 0) {//判断该计算是否会出现负数。
//strcmp函数可以判断两个字符数组的长度,两个字符数组是否相等,这样就可以精准判断谁大。
printf("-");
num1 = fun(str2, a);//交换位置
num2 = fun(str1, b);
}
else {
num1 = fun(str1, a);//被除数大于除数的情况
num2 = fun(str2, b);
}
//为了固定一个数组进行减法,所以要将两者进行比较长度,大小
for (int i = 0; i <= num1; i++) {//计算次数为较大的数所拥有的位数
if (a[i] < b[i]) {//出现借位情况
c[i] = a[i] - b[i] + 10;//避免计算结果过程出现负数,借一位就可以解决负数问题
a[i + 1]--;//相应的被借数减10
}
else {
c[i] = a[i] - b[i];
}
}
while (c[num1] == 0 && num1 > 0)//消去前导零,当结果数组最前一位为零才消去
num1--;//调整num1,
for (int i = num1; i >= 0; i--)//倒序输出
printf("%d", c[i]);
return 0;
}
三、高精度乘法(两者都为整数,都为正数)
这里指的高精度乘法为,一个大整数乘一个小整数。
1.储存数字方式与前两个相同,计算稍有不同。
例如:(A2A1A0)*(B1B0)=>C0=A0*(B1B0),C1=A1*(B1B0),C2=A2*(B1B0);
使用较大整数的每一位与小整数相乘。计算的每一位结果与大整数对应的位置相同
2.需要进行进位时要注意对10取余可以快速获得进位。
3.进行循环计算跳出时,使用是否有剩余进位辅助判断是否计算完成。
#include <stdio.h>
#include<string.h>
#define MAX 100010
int fun(char str[], int str2[])
{
char* p = str;
int num = strlen(str) - 1;
for (int i = num, j = 0; j <= num; i--, j++)
str2[i] = p[j] - '0';
return num + 1;
}
char str1[MAX];
int a[MAX] = { 0 };
int c[MAX] = { 0 };
int b;
int main()
{
scanf("%s", &str1);
scanf("%d", &b);
int num1 = fun(str1, a);
int j = 0;
int t = 0;
int i;//通过i进行了多少次操作可以获得计算结果的最高位数
//t用于保存进位,和确认是否可以跳出循环
for (i = 0; i < num1 || t != 0; i++) {//判断进行计算完成的条件为 t不等于零且计算结果的位数应大于或等与最大整数
if (i < num1)//小于大整数是为了最后一位进行计算时仍有进位的情况,需要将t复零才可以确定完成计算
t += a[i] * b;//加上之前进行进位
c[i] = t % 10;//将t对10取余才能确保计算结果为小于10的整数,
t /= 10;//保留大于10,用于下一位的进位
}
while (c[i] == 0 && i > 0)//除去前导零
i--;
for (; i >= 0; i--)//后导输出
printf("%d", c[i]);
return 0;
}
四、高精度除法。(两者都为正数且除数不为0)
与之前的高精度加法、减法、乘法不同,之前的都是从大整数的最小位进行计算,除法需要从高位进行计算。像小学的竖式计算一样,将大整数的每一位除以除数。需要保留上一位计算的余数继续计算。
#include <stdio.h>
#include <string.h>
#define MAX 100010
char str[MAX];
int a[MAX];
int b;
int c[MAX];
int main()
{
scanf("%s", &str);
scanf("%d", &b);
int num = strlen(str) - 1;
for (int i = 0, j = num; i <= num; i++, j--)//需要提前倒置除法
a[j] = str[i] - '0';
int t = 0;//记录余数
for (int i = num; i >= 0; i--)
{
t = t * 10 + a[i];//保留上一位的余数进行计算
c[i] = t / b;//将大整数的每一位除以小整数
t %= b;//对b取余可以获得当前计算所留下的余数
}
while (c[num] == 0 && num > 1)//消去前导零
num--;
for (int i = num; i >= 0; i--)//逆置输出
printf("%d", c[i]);
printf("\n%d", t);
return 0;
}