【C++提高】高精度减法

📅 2026/7/18 21:00:41
【C++提高】高精度减法
前言一天你在洛谷开心的刷着算法题突然见到这么一道题。P2142其余背景与【C提高】高精度加法-CSDN博客一样。所以就不打了:#:^~^:#:一、高精度减法概述在计算机科学中高精度运算是指处理超出基本数据类型如int、long long表示范围的整数或浮点数的运算。与高精度加法类似减法也需要通过模拟手工竖式计算的方式逐位处理但涉及借位操作逻辑更为复杂。二、代码整体结构与思路首先我们来看代码#includebits/stdc.h using namespace std; string a,b; int main(){ cinab; bool isffalse; if(a.length()!b.length()){ if(a.length()b.length()){ string ta; ab; bt; isftrue; } } else{ if(ab){ cout0; return 0; } else if(ba){ string ta; ab; bt; isftrue; } } reverse(a.begin(),a.end()); reverse(b.begin(),b.end()); string ans; int jw0; for(int i0;ia.size();i){ int t1a[i]-0; int t2ib.size()?b[i]-0:0; int ret1-t2-jw; if(re0){ re10; jw1; } else{ jw0; } ans(re0); } reverse(ans.begin(),ans.end()); int i; for(i0;ians.size();i){ if(ans[i]!0){ break; } } if(isf){ cout-; } for(;ians.size();i){ coutans[i]; } return 0; }这段代码实现了两个非负大整数以字符串形式输入的减法运算。其核心思路可以概括为以下几个步骤输入与预处理读入两个字符串a和b分别代表被减数和减数。大小比较与符号确定确保a代表的数字不小于b否则交换两者并记录结果为负。反转字符串将两个字符串反转以便从低位开始逐位计算。逐位相减与借位处理模拟手工减法处理借位。结果反转与去除前导零将结果字符串反转回正常顺序并去除前导零。输出结果根据符号标志输出最终结果。三、解析1. 输入与预处理cinab; bool isffalse;代码使用string类型存储输入避免了数值范围的限制。isf是一个布尔标志初始为false用于记录最终结果是否为负数。2. 大小比较与符号确定核心逻辑这是高精度减法中最关键且容易出错的部分。代码通过两个层次进行比较长度比较如果两个字符串长度不同那么长度更长的数字显然更大假设没有前导零。如果a.length() b.length()说明被减数小于减数需要交换a和b并将isf设为true表示结果为负。等长时的字典序比较如果长度相同则直接使用字符串的字典序比较运算符和。对于两个长度相同且没有前导零的数字字符串字典序比较与数值比较是等价的。这里分三种情况a b两数相等差为 0直接输出 0 并结束程序。b a减数更大需要交换并标记结果为负。a b被减数更大保持原样结果为正。思考为什么先比较长度再比较字典序因为如果长度不同字典序比较可能得出错误结论例如 9 10 在字典序中成立但数值上 9 10。3. 字符串反转reverse(a.begin(),a.end()); reverse(b.begin(),b.end());将字符串反转是为了让计算从最低位个位开始。在字符串中数字的高位存储在索引 0 的位置低位在末尾。反转后a[0]和b[0]就分别代表原数字的个位方便我们使用循环顺序处理。4. 逐位相减与借位处理核心计算string ans; int jw0; // 借位标志 for(int i0;ia.size();i){ int t1a[i]-0; // 当前位被减数 int t2ib.size()?b[i]-0:0; // 当前位减数若b已耗尽则为0 int ret1-t2-jw; // 当前位计算结果减去上一位的借位 if(re0){ re10; // 不够减向高位借10 jw1; // 标记借位发生 } else{ jw0; // 够减无借位 } ans(re0); // 将当前位数字转为字符存入结果 }变量说明jw借位标志jw是“借位”拼音首字母。jw1表示上一位计算时发生了借位当前位需要多减 1。t1当前位被减数的数值。t2当前位减数的数值。由于b可能比a短经过预处理后a长度不小于b所以需要判断i是否在b的范围内超出则取 0。re当前位初步计算结果t1 - t2 - jw。借位逻辑如果re 0说明当前位不够减需要向高位借 1相当于借 10所以执行re 10并将jw设为 1影响下一位计算。如果re 0说明够减jw设为 0。示例计算 52 - 17a52, b17。反转后a25, b71。- i0: t12, t21, jw0, re1 (0) → ans1, jw0- i1: t15, t27, jw0, re-2 (0) → re8, jw1 → ans18反转 ans 得到 81去除前导零后为 81结果正确。5. 结果反转与去除前导零reverse(ans.begin(),ans.end()); int i; for(i0;ians.size();i){ if(ans[i]!0){ break; } }计算完成后ans中存储的是反转的结果低位在前。需要再次反转得到正常顺序。然后需要去除前导零。例如计算 100 - 99 得到 01反转后为 10但我们需要输出 10 而不是 010。循环找到第一个非零字符的位置i。边界情况如果所有位都是零即结果为 0循环结束后i将等于ans.size()。在后续输出中for(;ians.size();i)将不会执行任何输出但代码在之前相等判断中已经处理了结果为 0 的情况直接输出 0 并返回。这里的设计是为了处理像 100 - 100 这种经过交换后可能产生全零结果的情况吗实际上在之前的判断中如果a b程序已经返回不会执行到这里。因此这里的去零逻辑是安全的。6. 输出结果if(isf){ cout-; } for(;ians.size();i){ coutans[i]; }如果之前标记了结果为负isf true先输出负号。然后从第一个非零位开始输出结果字符串。结尾当你拿出新学习的算法去做着到题时……恭喜你学会了一个新的算法。