轻松掌握高精度计算:用数组处理超大数字

1.为什么需要高精度计算?

想象你要计算:

  • 银行账户余额(可能有几十位数字)
  • 宇宙中的星星总数(约10²⁴颗)
  • 100的阶乘(100! = 933262154439441526816992388562667…)

这些数字太大了,连计算器都显示不出来!普通计算机的整数类型最多只能存19位数字,这时候就需要"高精度计算"了。

2.核心思想:用数组模拟数字

其实方法特别简单:

  1. 把超长数字拆成单个数字存入数组
    • 例如:12345 存为 [1,2,3,4,5]
  2. 然后列竖式逐位计算即可
  3. 处理好进位/借位就搞定啦!

3.三种基本运算详解

3.1➕加法

  123
+ 456
-----
  579
  1. 个位:3+6=9(不进位)
  2. 十位:2+5=7
  3. 百位: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
  1. 个位:3<7,向十位借1→13-7=6
  2. 十位:1被借走变0,0<8,向百位借1→10-8=2
  3. 百位: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
  1. 3×5=15(写5进1)
  2. 2×5+1=11(写1进1)
  3. 3×4=12(错位相加)
  4. 2×4+1=9
  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);

}
//计算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.为什么这样设计?

  1. 数组存储:可以自由扩展数字长度
  2. 逐位计算:完全模拟人类计算习惯
  3. 处理进位:这是最关键的部分
  4. 结果数组逆置:答案在数组中是反向的

5.补充练习

如今我们已经掌握了高精度的加减乘法运算,如何实现高精度的阶乘之和?
[NOIP 1998 普及组] 阶乘之和

感谢您的阅读和关注! (˶╹ꇴ╹˶)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值