1. 概念引入:为什么需要高精度加法?

我们知道,C++ 中的基本数据类型如 intlong long 可以存储的整数范围是有限的。

  • int 通常最大只能存储约 2×1092 \times 10^9 (20 亿)。
  • long long 最大能存储约 9×10189 \times 10^{18} (9 百亿亿)。

但是,在某些计算场景中(例如信息学竞赛、密码学等),我们需要处理超过数十位、数百位,甚至上千位的巨大整数。这些数字远远超出了任何内置数据类型的存储范围。

高精度加法,就是为了解决两个或多个超大整数相加的问题。它的核心思想是模拟我们日常进行竖式加法的过程

2. 核心思路:模拟竖式加法

在小学,我们学习的竖式加法遵循以下规则:

  1. 对齐: 将两个加数按个位、十位、百位等对齐。
  2. 逐位相加: 从个位开始,将对应位的数字相加。
  3. 进位: 如果当前位的和大于或等于 10,则将进位值(和除以 10 的商)带到下一位。

高精度加法的程序实现正是完全模拟这个过程:

  • 存储: 使用数组或字符串存储大整数的每一位数字。
  • 计算: 从低位(个位)开始,向高位(左侧)逐位相加,并处理进位。
  • 输出: 从最高位(左侧)开始输出结果。

3. 核心实现步骤与代码解析

我们将使用您提供的代码作为示例,来剖析高精度加法的核心实现。

步骤 A:大整数的输入与存储

为了方便计算,我们需要将输入的字符串形式的大整数,逆序存储到整型数组中。

为什么需要逆序存储? 逆序存储后,数组的下标就可以对应位数。例如,a1[1] 永远是个位,a1[2] 永远是十位,这使得我们从数组下标 1 开始循环计算时,天然就是从低位向高位进行。

位数 个位 十位 百位 ...
正向数字 ... N3N_3 N2N_2 N1N_1
数组下标 1 2 3 ...

代码实现:

char a[200],b[200];
cin >> a >> b; // 输入字符串形式的数字

int lena = strlen(a); // 字符串 a 的长度
int lenb = strlen(b); // 字符串 b 的长度

// 将字符串 a 逆序存入数组 a1
for(int i = 1; i <= lena; i++)
{
    // a[lena-i] 是从字符串末尾往前数的字符(即低位)
    // -'0' 将字符 '0'-'9' 转换为整数 0-9
    a1[i] = a[lena-i] - '0';
}
// 对 b1 进行相同的操作
for(int i = 1; i <= lenb; i++)
{
    b1[i] = b[lenb-i] - '0';
}
// 此时,a1[1]和b1[1]都是个位
步骤 B:模拟竖式逐位相加与进位处理

这是高精度加法的核心循环,它负责模拟从低位到高位的加法过程,并实时处理进位。

关键变量:

  • lenc: 当前计算到的位数(即结果数组 c 的下标)。
  • maxlen: 两个加数中较长的那个长度,决定了主循环至少需要执行的次数。
  • x: 进位值 (Carry),初始化为 0。

代码实现:

int lenc = 1;
int maxlen = max(lena, lenb);
int x = 0; // x 用来存储进位

// 当位数小于等于最长长度时,循环进行逐位相加
while(lenc <= maxlen)
{
    // 1. 当前位的和 = a的当前位 + b的当前位 + 上一位的进位
    // 注意:a1[lenc]或b1[lenc]如果越界(如某个数较短),数组初始化为0,不影响结果
    c[lenc] = a1[lenc] + b1[lenc] + x;
    
    // 2. 更新进位:和除以 10 的商就是新的进位值
    x = c[lenc] / 10;
    
    // 3. 存储当前位的结果:和对 10 取余就是当前位应存储的数字
    c[lenc] %= 10;
    
    // 移向下一位
    lenc++;
}

// 4. 处理最高位的进位
c[lenc] = x; // 循环结束后,如果 x > 0,则需要在新的一位存储进位
步骤 C:处理结果长度并输出

由于结果数组 c 是逆序存储的,我们需要逆序输出

此外,需要特别处理结果最高位的前导零。如果两个数的最高位相加后没有进位,那么 c[lenc](即最高位下一位)可能为 0,且这个 0 是无效的。

代码实现:

// 处理最高位的进位(如果 x > 0,lenc++ 已经指向了实际的最高位)
// 如果最高位是 0 且结果位数不止 1 位(防止结果是 0 时被错误缩短)
while(c[lenc] == 0 && lenc > 1)
{
    lenc--; // 缩短结果长度,跳过最高位的无效 0
}

// 逆序输出数组 c
for(int i = lenc; i >= 1; i--)
{
    cout << c[i];
}

return 0;

4. 完整代码

#include <bits/stdc++.h> // 包含常用的头文件,如 iostream 和 cstring

using namespace std;

// 全局数组,用于存储大整数的每一位(逆序存储)
// a1, b1 存储输入,c 存储结果
int a1[200], b1[200], c[200]; 

int main()
{
	// 字符串用于输入原始大整数
	char a[200], b[200];
	cin >> a >> b;
	
	int lena = strlen(a); // 第一个数 a 的长度
	int lenb = strlen(b); // 第二个数 b 的长度
	
	// --- 步骤 A: 逆序存储数字 ---
	
	// 将 a 逆序存入 a1,从下标 1 开始存个位
	for(int i = 1; i <= lena; i++)
	{
		a1[i] = a[lena - i] - '0';
	}
	
	// 将 b 逆序存入 b1,从下标 1 开始存个位
	for(int i = 1; i <= lenb; i++)
	{
		b1[i] = b[lenb - i] - '0';
	}
	
	// --- 步骤 B: 模拟竖式加法和进位 ---
	
	int lenc = 1; // 当前计算的位数(结果数组 c 的下标)
	int maxlen = max(lena, lenb); // 两个加数中最长的长度
	int x = 0; // 进位值 (Carry)
	
	while(lenc <= maxlen)
	{
		// 1. 计算当前位和:对应位数字 + 上一位的进位
		c[lenc] = a1[lenc] + b1[lenc] + x;
		
		// 2. 更新进位:新的进位是和除以 10 的商
		x = c[lenc] / 10;
		
		// 3. 存储当前位数字:当前位数字是和对 10 取余
		c[lenc] %= 10;
		
		lenc++; // 移向下一位
	}
	
	// 4. 处理最终的进位
	c[lenc] = x;
	
	// --- 步骤 C: 处理前导零并输出结果 ---
	
	// 如果最高位是 0 且结果长度大于 1(即不是结果 0),则缩短长度
	while(c[lenc] == 0 && lenc > 1)
	{
		lenc--;
	}
	
	// 逆序输出结果
	for(int i = lenc; i >= 1; i--)
	{
		cout << c[i];
	}
	cout << endl; // 输出换行符,保持良好习惯
	
	return 0;
}

0 条评论

目前还没有评论...