轻松掌握高精度计算:用数组处理超大数字
1.为什么需要高精度计算?
想象你要计算:
- 银行账户余额(可能有几十位数字)
- 宇宙中的星星总数(约10²⁴颗)
- 100的阶乘(100! = 933262154439441526816992388562667…)
这些数字太大了,连计算器都显示不出来!普通计算机的整数类型最多只能存19位数字,这时候就需要"高精度计算"了。
2.核心思想:用数组模拟数字
其实方法特别简单:
- 把超长数字拆成单个数字存入数组
- 例如:12345 存为 [1,2,3,4,5]
- 然后列竖式逐位计算即可
- 处理好进位/借位就搞定啦!
3.三种基本运算详解
3.1➕加法
123
+ 456
-----
579
- 个位:3+6=9(不进位)
- 十位:2+5=7
- 百位:1+4=5
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
//逆置函数
void reverse(char* result,int len)
{
char* left = result, * right = result + len - 1;
while (left < right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
//模拟竖式计算
void High_precision_Add(char* num1,char*num2,char* result)
{
int i = 0, carry = 0;
int len1 = strlen(num1), len2 = strlen(num2);
int max = len1 > len2 ? len1 : len2;
int min = len1 < len2 ? len1 : len2;
for (i=0;i<max;i++)
{
if (len1 - 1 - i < 0)
{
result[i] = ( num2[len2 - 1 - i] - '0' + carry) % 10 + '0';
carry = ( num2[len2 - 1 - i] - '0' + carry) / 10;
}
else if (len2 - 1 - i < 0)
{
result[i] = (num1[len1 - 1 - i] - '0' + carry) % 10 + '0';
carry = (num1[len1 - 1 - i] - '0' + carry) / 10;
}
else
{
result[i] = (num1[len1 - 1 - i] - '0' + num2[len2 - 1 - i] - '0' + carry) % 10 + '0';
carry = (num1[len1 - 1 - i] - '0' + num2[len2 - 1 - i] - '0' + carry) / 10;
}
}
if (carry != 0)
{
result[i] = carry + '0';
result[i + 1] = 0;
}
else
{
result[i] = 0;
i--;
}
reverse(result, i+1);
}
int main()
{
char num1[20000], num2 [20000], result[20000];
scanf("%s%s", num1, num2);
High_precision_Add(num1, num2, result);//num1+num2=result
printf("%s", result);
return 0;
}
3.2➖ 减法(记得借位)
523
- 187
-----
336
- 个位:3<7,向十位借1→13-7=6
- 十位:1被借走变0,0<8,向百位借1→10-8=2
- 百位:4-1=3
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
//逆置函数
void reverse(char* result, int len)
{
char* left = result, * right = result + len - 1;
while (left < right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
//模拟竖式计算
void High_precision_Sub(char* num1, char* num2, char* result)
{
int i = 0;
int len1 = strlen(num1), len2 = strlen(num2);
if (len1 > len2)
{
for (i = 0;i < len1;i++)
{
if (num1[len1 - 1 - i] < '0')
{
num1[len1 - 1 - i] = '9';
num1[len1 - 1 - i - 1]--;
}
if (i < len2)
{
if (num1[len1-1-i] >= num2[len2-1-i])
{
result[i] = num1[len1-1-i] - num2[len2-1-i]+'0';
}
else
{
result[i] = num1[len1-1-i] + 10 - num2[len2 - 1 - i]+'0';
num1[len1 - 1 - i - 1]--;
}
}
else
{
result[i] = num1[len1 - 1 - i];
}
}
char* p = result + i - 1;
if (*p == '0')
{
*p = 0;
i--;
}
else
*(p + 1) = 0;
reverse(result, i);
}
else if (len2 > len1)
{
for (i = 0;i < len2;i++)
{
if (num2[len2 - 1 - i] < '0')
{
num2[len2 - 1 - i] = '9';
num2[len2 - 1 - i - 1]--;
}
if (i < len1)
{
if (num2[len2-1-i] >= num1[len1-1-i])
{
result[i] = num2[len2-1-i] - num1[len1-1-i] + '0';
}
else
{
result[i] = num2[len2 - 1 - i] + 10 - num1[len1 - 1 - i] + '0';
num2[len2 - 1 - i - 1]--;
}
}
else
{
result[i] = num2[len2 - 1 - i];
}
}
char* p = result + i - 1;
if (*p == '0')
{
*p = '-';
*(p + 1) = 0;
}
else
{
*(p + 1) = '-';
*(p + 2) = 0;
i++;
}
reverse(result, i);
}
else
{
if (strcmp(num1, num2) > 0)
{
for (i = 0;i < len1;i++)
{
if (num1[len1 - 1 - i] < '0')
{
num1[len1 - 1 - i] = '9';
num1[len1 - 1 - i - 1]--;
}
if (i < len2)
{
if (num1[len1-1-i] >= num2[len2-1-i])
{
result[i] = num1[len1-1-i] - num2[len2-1-i] + '0';
}
else
{
result[i] = num1[len1 - 1 - i] + 10 - num2[len2 - 1 - i] + '0';
num1[len1 - 1 - i - 1]--;
}
}
else
{
result[i] = num1[len1 - 1 - i];
}
}
char* p = result + i - 1;
if (*p == '0')
{
*p = 0;
i--;
}
else
*(p + 1) = 0;
reverse(result, i);
}
else if (strcmp(num1, num2) < 0)
{
for (i = 0;i < len2;i++)
{
if (num2[len2 - 1 - i] < '0')
{
num2[len2 - 1 - i] = '9';
num2[len2 - 1 - i - 1]--;
}
if (i < len1)
{
if (num2[len2-1-i] >= num1[len1-1-i])
{
result[i] = num2[len2-1-i] - num1[len1-1-i] + '0';
}
else
{
result[i] = num2[len2 - 1 - i] + 10 - num1[len1 - 1 - i] + '0';
num2[len2 - 1 - i - 1]--;
}
}
else
{
result[i] = num2[len2 - 1 - i];
}
}
//处理'-'号
char* p = result + i - 1;
if (*p == '0')
{
*p = '-';
*(p + 1) = 0;
}
else
{
*(p + 1) = '-';
*(p + 2) = 0;
i++;
}
reverse(result, i);
}
else
{
result[0] = '0';
result[1] = 0;
}
}
}
int main()
{
char num1[20000], num2[20000], result[20000];
scanf("%s%s", num1, num2);
High_precision_Sub(num1, num2, result);//num1-num2=result
printf("%s", result);
return 0;
}
3.3✖️ 乘法
23
× 45
-----
115
+ 92
-----
1035
- 3×5=15(写5进1)
- 2×5+1=11(写1进1)
- 3×4=12(错位相加)
- 2×4+1=9
- 最后合并结果
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
//逆置函数
void reverse(char* result, int len)
{
char* left = result, * right = result + len - 1;
while (left < right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
//引入高精度加法
void High_precision_Add(char* num1, char* num2, char* result)
{
int i = 0, carry = 0;
int len1 = strlen(num1), len2 = strlen(num2);
int max = len1 > len2 ? len1 : len2;
int min = len1 < len2 ? len1 : len2;
for (i = 0;i < max;i++)
{
if (len1 - 1 - i < 0)
{
result[i] = (num2[len2 - 1 - i] - '0' + carry) % 10 + '0';
carry = (num2[len2 - 1 - i] - '0' + carry) / 10;
}
else if (len2 - 1 - i < 0)
{
result[i] = (num1[len1 - 1 - i] - '0' + carry) % 10 + '0';
carry = (num1[len1 - 1 - i] - '0' + carry) / 10;
}
else
{
result[i] = (num1[len1 - 1 - i] - '0' + num2[len2 - 1 - i] - '0' + carry) % 10 + '0';
carry = (num1[len1 - 1 - i] - '0' + num2[len2 - 1 - i] - '0' + carry) / 10;
}
}
if (carry != 0)
{
result[i] = carry + '0';
result[i + 1] = 0;
}
else
{
result[i] = 0;
i--;
}
reverse(result, i + 1);
}
//计算num2的各位乘num1
void High_precision_Mul_single(char* num1, char num2,int weight, char* result)//weight表示num2各位的权重
{
int i = 0;
int len = strlen(num1),carry=0;
for (i =0;i <len;i++)
{
result[i] = ((num2 - '0') * (num1[len - 1 - i] - '0') + carry) % 10+'0';
carry = ((num2 - '0') * (num1[len - 1 - i] - '0') + carry) / 10;
}
if (carry == 0)
result[i] = 0;
else
{
while (carry)
{
result[i] = carry % 10+'0';
carry /= 10;
i++;
}
result[i] = 0;
}
reverse(result, i);
int j = 1;
for ( j = 1;j <= weight;j++)
{
result[i-1+j] = '0';
}
result[i - 1 + j] = 0;
}
//模拟竖式计算num1*num2
void High_precision_Mul(char* num1, char* num2, char* result)
{
if (*num1 == '0' || *num2 == '0')
{
result[0] = '0';
result[1] = 0;
return;
}
int i = 0, carry = 0, flag = 0;
int len1 = strlen(num1), len2 = strlen(num2);
char* p1 = num1, * p2 = num2;
if (*num1 != '-' && *num2 != '-')
{
flag = 1;
}
else if (*num1 == '-' && *num2 == '-')
{
len1--;
len2--;
p1++;
p2++;
flag=1;
}
else
{
if (*num1 == '-')
{
len1--;
p1++;
}
else
{
len2--;
p2++;
}
flag = 0;
}
char result1[20000] = { '0' }, result2[20000] = { '0' };
if (len1 > len2)
{
for (i = 0;i < len2;i++)
{
High_precision_Mul_single(p1, p2[len2 - 1 - i],i, result1);
High_precision_Add(result1, result2, result);
strcpy(result2, result);
}
}
else
{
for (i = 0;i < len1;i++)
{
High_precision_Mul_single(p2, p1[len1 - 1 - i], i, result1);
High_precision_Add(result1, result2, result);
strcpy(result2, result);
}
}
if (flag == 0)
{
int len = strlen(result);
reverse(result, len);
*(result + len) = '-';
*(result + len + 1) = 0;
reverse(result, strlen(result));
}
}
int main()
{
char num1[20000], num2[20000], result[20000];
scanf("%s%s", num1, num2);
High_precision_Mul(num1, num2, result);//num1*num2=result
printf("%s", result);
return 0;
}
大大减少计算时间
4.为什么这样设计?
- 数组存储:可以自由扩展数字长度
- 逐位计算:完全模拟人类计算习惯
- 处理进位:这是最关键的部分
- 结果数组逆置:答案在数组中是反向的
5.补充练习
如今我们已经掌握了高精度的加减乘法运算,如何实现高精度的阶乘之和?
[NOIP 1998 普及组] 阶乘之和
感谢您的阅读和关注! (˶╹ꇴ╹˶)