本课时有配套视频讲解,购买后即可观看(永久有效)
高精度减运算
一、课上练习
编程练习
- 高精度减运算:L3021
二、知识总结
✨ 核心思想
高精度运算是指在计算机科学和数值计算中,能够处理和运算非常大的数字的技术。在编程中,普通的整数类型(如 int 或 long)有其最大值的限制,超过这个范围的运算就会溢出。为了处理更大的数字,比如在大数加密、科学计算或经济模型分析中,就需要用到高精度(大数)运算。
高精度减运算与加运算类似,模拟的是手算竖式减法的过程。不同的是,减法需要额外处理借位和负数结果的情况。
✨ 算法原理
高精度减法的实现步骤如下:
- 字符串存储:使用
string类变量来存储整个大数,字符串的每个字符可以直接映射到每一位上的数字。 - 大小比较与符号处理:判断两个高精度数的大小,若被减数比减数小,则记录负号并交换减数与被减数,确保始终用大数减小数。
- 逆序存入数组:将每一位上的数字逆序存储进数组中,方便从低位开始运算。
- 逐位相减并借位:从低位到高位完成对应位相减的操作,在减的过程中进行借位处理。如果某一位不够减,则向高一位借 1(相当于当前位加 10)。
- 清除前导零并输出:清除结果中的前导零,逆序输出按位减的结果。
✨ 代码实现
以下是高精度减法的完整实现,注意代码中对被减数小于减数情况的特殊处理:
高精度减运算代码示例
1#include <iostream>
2using namespace std;
3
4// 将字符串s中的数字转换为整数数组a,每位倒序存储
5void convert(int a[], string s){
6 int len = s.length(); // 获取字符串长度
7 for (int i = 0; i < len; i++) {
8 a[i] = s[len - 1 - i] - '0'; // 将字符串的每个字符从后向前转换为整数并存入数组
9 }
10}
11
12// 打印整数数组a表示的数字,数组中数字是倒序存储的
13void print(int a[], int len) {
14 for (int i = 0; i < len; i++) {
15 cout << a[len - 1 - i]; // 从后向前打印数组元素,以正确顺序显示数字
16 }
17 cout << endl;
18}
19
20// 实现两个数字的高精度减法,结果存储在result数组中,len为结果的长度
21void subtraction(int a1[], int len1, int a2[], int len2, int result[], int &len) {
22 for (int i = 0; i < len; i++) {
23 result[i] += a1[i] - a2[i]; // 将对应位相减,并加上之前的借位
24 if (result[i] < 0) { // 检查是否需要借位
25 result[i + 1]--; // 借位
26 result[i] += 10; // 当前位加上基数10
27 }
28 }
29 while (result[len - 1] == 0 && len > 1) { // 清除结果前导0,但至少保留一位数字
30 len--;
31 }
32}
33
34int main() {
35 string s1, s2;
36 int a1[105] = {0}; // 存储第一个数的数组,初始化为0
37 int a2[105] = {0}; // 存储第二个数的数组,初始化为0
38 cin >> s1 >> s2;
39
40 if (s1.length() < s2.length() || (s1.length() == s2.length() && s1 < s2)) {
41 swap(s1, s2); // 如果s1小于s2,交换它们以确保结果为正
42 cout << '-'; // 输出负号
43 }
44 int len1 = s1.length(); // 第一个字符串的长度
45 int len2 = s2.length(); // 第二个字符串的长度
46 convert(a1, s1); // 转换第一个字符串为整数数组
47 convert(a2, s2); // 转换第二个字符串为整数数组
48
49 int result[105] = {0}; // 存储结果的数组,初始化为0
50 int len = len1; // 设置结果长度初值为len1
51
52 subtraction(a1, len1, a2, len2, result, len); // 执行减法运算
53
54 print(result, len); // 打印结果
55 return 0;
56}✨ 执行示例
以计算 503 - 287 为例:
第一步:字符串存储
- 输入 s1 = "503",s2 = "287"
第二步:大小比较
- s1.length() == s2.length() 且 s1 > s2("503" > "287"),无需交换,结果为正。
第三步:逆序存入数组
- a1[] = {3, 0, 5}(对应个位3、十位0、百位5)
- a2[] = {7, 8, 2}(对应个位7、十位8、百位2)
第四步:逐位相减并借位(len = 3)
| 步骤 | 位置i | a1[i] | a2[i] | result[i]计算 | 是否借位 | result[i]最终 | result[i+1]借位 |
|---|---|---|---|---|---|---|---|
| 1 | 0(个位) | 3 | 7 | 0+3-7=-4 | 是 | -4+10=6 | -1 |
| 2 | 1(十位) | 0 | 8 | -1+0-8=-9 | 是 | -9+10=1 | -1 |
| 3 | 2(百位) | 5 | 2 | -1+5-2=2 | 否 | 2 | 0 |
result[] = {6, 1, 2},len = 3
第五步:清除前导零并输出
- result[2] = 2 不为零,无需清除前导零。
- 逆序输出:
216
验证:503 - 287 = 216,正确。
再看一个需要输出负号的例子:计算 123 - 456
- s1.length() == s2.length() 且 s1 < s2("123" < "456"),交换后 s1="456"、s2="123",输出负号
-。 - 按 456 - 123 计算得 333,最终输出
-333。
✨ 解题步骤详解
当你遇到一道高精度减法题时,可以按以下步骤思考:
- 判断大小关系:先比较两个大数的大小。比较规则是:先比长度,长度长的更大;长度相同则按字典序比较字符串。
- 确定符号:如果被减数小于减数,记录负号并交换两个数,保证始终用大数减小数。
- 逆序转换:将两个字符串逆序存入数组。
- 逐位相减:从低位开始,对应位相减。如果当前位不够减(结果为负),则向高位借 1,当前位加 10。
- 清除前导零:结果高位可能出现多余的零,需要从高位向低位清除,但至少保留一位(结果为 0 时输出 "0")。
- 输出结果:如果之前记录了负号,先输出负号,再逆序输出结果数组。
✨ 常见错误
- 忘记处理负数情况:当被减数小于减数时,如果不交换并输出负号,计算结果会出错。
- 大小比较写错:比较两个大数不能直接用
<比较字符串值(只有长度相同时字符串比较才等价于数值比较),必须先比长度再比字符串。 - 前导零未清除:例如
1000 - 999 = 001,如果不清除前导零就会输出001而不是1。 - 清除前导零时删过头:当结果为 0 时(如
100 - 100),必须保留至少一位,否则什么都不输出。 - 借位时忘记在高位减 1:借位操作包含两步:当前位加 10 且 高一位减 1,缺一不可。
三、课后练习
编程练习
- 大整数减法:L3022