1. 概念引入与基本原则

高精度减法用于计算超大整数之间的减法。与高精度加法类似,其核心思想是模拟我们日常进行竖式减法的过程

基本原则(核心区别):

在小学,我们学习竖式减法时,必须保证被减数大于或等于减数

  • 如果 AB\text{A} \ge \text{B},结果为 AB\text{A} - \text{B}
  • 如果 A<B\text{A} < \text{B},我们需要计算 BA\text{B} - \text{A},并在结果前添加一个负号(-)。

因此,高精度减法的第一步是确定被减数和减数,并预判结果的正负号

2. 实现步骤与代码解析

我们将以您提供的代码为例,分步解析高精度减法的实现。

步骤 A:输入、判断大小与预处理负号

程序首先输入两个数 AABB(以字符串形式),然后判断 AABB 的大小。

代码解析:

char a[200],b[200];
cin >> a >> b;
int lena = strlen(a);
int lenb = strlen(b);

// 判断 A 是否小于 B:
// 1. A 的长度 < B 的长度,或者
// 2. 长度相等,但字符串 A 小于 B (strcmp(a,b)<0)
if (lena < lenb || (lena == lenb && strcmp(a, b) < 0))
{
    swap(a, b); // 交换 A 和 B,确保 A >= B
    cout << "-"; // 提前输出负号
}

// 重新获取可能的交换后的长度
lena = strlen(a);
lenb = strlen(b);
  • 目标: 确保后面的计算始终是 较大数减去较小数
  • 如果发现 A<BA < B,则交换 A,BA, B,并立即输出负号,以确保最终结果正确。
步骤 B:大整数的逆序存储

与加法相同,我们使用逆序存储,将个位放在数组的低位(下标 1)。

// 将 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';
}
// 此时,a1[i] 存储的是 A 的第 i 位数字,b1[i] 存储的是 B 的第 i 位数字。
步骤 C:模拟竖式逐位相减与借位处理(核心)

这是减法的核心循环,它负责模拟从低位到高位的减法,并实时处理借位

关键逻辑: 从低位开始,逐位相减 a1[lenc]b1[lenc]\text{a1}[\text{lenc}] - \text{b1}[\text{lenc}]

  1. 无需借位: 如果 a1[lenc]b1[lenc]\text{a1}[\text{lenc}] \ge \text{b1}[\text{lenc}],则直接相减。
  2. 需要借位: 如果 a1[lenc]<b1[lenc]\text{a1}[\text{lenc}] < \text{b1}[\text{lenc}],则需要向高位借 1。
    • 借位操作:将当前位 a1[lenc]\text{a1}[\text{lenc}] 加上 10。
    • 高位处理:将高一位 a1[lenc+1]\text{a1}[\text{lenc}+1] 减去 1(即被减数的下一位减 1)。

代码解析:

int lenc = 1; // 当前计算到的位数
int maxlen = max(lena, lenb); // 决定循环次数

while (lenc <= maxlen)
{
    // 1. 判断是否需要借位
    if (a1[lenc] < b1[lenc])
    {
        a1[lenc] += 10;   // 当前位借 1,数值上加 10
        a1[lenc + 1]--;   // 高一位被减数 (a1[lenc+1]) 减 1
    }
    
    // 2. 计算当前位的结果
    c[lenc] = a1[lenc] - b1[lenc];
    
    lenc++; // 移向下一位
}

// 注意:这段代码**没有显式的进位变量 x**。
// 因为借位操作是直接修改了被减数 a1[lenc+1],将借位的影响提前传递给了下一位的计算。
步骤 D:处理前导零与输出结果

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

减法结果可能存在多个高位零(例如 1000999=11000 - 999 = 1),这些零是无效的,需要跳过。

// lenc 在循环结束后会多加 1,所以它是结果最高位再往上的一位。
// lenc-- 是因为上一个循环多加了 1,c[lenc] 在这里已经是 0 了。
while (c[lenc] == 0 && lenc > 1)
{
    lenc--; // 缩短结果长度,跳过最高位的无效 0
}

// 逆序输出数组 c,从最高位开始
for (int i = lenc; i >= 1; i--)
{
    cout << c[i];
}
return 0;

3. 完整代码(高精度减法)

#include <bits/stdc++.h>
using namespace std;

int a1[200],b1[200],c[200]; // 数组用于逆序存储大整数的每一位

int main()
{
	char a[200],b[200];
	cin >> a >> b;
	int lena = strlen(a);
	int lenb = strlen(b);
	
	// --- 步骤 A: 预处理负号,确保 A >= B ---
	if (lena < lenb || (lena == lenb && strcmp(a, b) < 0))
	{
		swap(a, b); // 交换,确保 A >= B
		cout << "-"; // 输出负号
	}
	
	// 重新获取可能交换后的长度
	lena = strlen(a);
	lenb = strlen(b);
	
	// --- 步骤 B: 逆序存储 ---
	for (int i = 1; i <= lena; i++)
	{
		a1[i] = a[lena - i] - '0';
	}
	
	for (int i = 1; i <= lenb; i++)
	{
		b1[i] = b[lenb - i] - '0';
	}
	
	// --- 步骤 C: 模拟竖式减法(借位)---
	int lenc = 1;
	int maxlen = max(lena, lenb);
	
	while (lenc <= maxlen)
	{
		// 如果当前位 a1 < b1,进行借位
		if (a1[lenc] < b1[lenc])
		{
			a1[lenc] += 10; // 当前位加 10
			a1[lenc + 1]--; // 高一位减 1
		}
		
		// 计算当前位结果
		c[lenc] = a1[lenc] - b1[lenc];
		
		lenc++;
	}
	
	// --- 步骤 D: 处理前导零与输出 ---
	
	// lenc-- 确保我们从最高的非零位开始输出
	lenc--; // 因为循环中 lenc 多加了一次
	while (c[lenc] == 0 && lenc > 1)
	{
		lenc--;
	}
	
	for (int i = lenc; i >= 1; i--)
	{
		cout << c[i];
	}
	
	// 额外处理:如果结果是 0,只输出一个 0
	if (lenc == 1 && c[1] == 0) {
	    // 这种情况已经被上述代码正确处理,因为 lenC 会停在 1,且 c[1] 为 0,输出 c[1]
	}
	
	return 0;
}

0 条评论

目前还没有评论...