高精度加减

张开发
2026/4/16 1:04:56 15 分钟阅读

分享文章

高精度加减
一、高精度运算核心前提理解“数组模拟数位”在开始具体运算前我们首先要解决一个关键问题如何存储超大数答案很简单——用数组或容器拆分存储每一位数字这里有一个黄金存储规则低位在前高位在后。举个例子数字1234常规思维是按“高位到低位”存储为[1,2,3,4]但在高精度运算中我们会存储为[4,3,2,1]数组下标0对应个位下标1对应十位下标2对应百位下标3对应千位。为什么要这样存储原因很简单加减运算都是从最低位个位开始的低位在前的存储方式能让我们从数组下标0开始逐位运算进位、借位时只需操作相邻下标无需移动整个数组代码逻辑更简洁运算效率也更高。后续输出结果时只需逆序遍历数组即可得到正常的数字顺序。另外由于我们通常用字符串输入超大数直接输入数字会溢出所以需要先将字符串转换为数组转换时注意字符串的第一个字符是数字的最高位需要逆序存入数组同时通过字符减去0的方式将字符转换为对应的数字。二高精度加法逐位相加处理进位2.1 核心原理复刻竖式加法我们小学学的竖式加法就是高精度加法的核心逻辑以1234 5678为例1234 5678 ------ 6912拆解下来核心步骤只有3步对齐将两个数字的最低位对齐对应数组下标0的位置逐位相加从最低位数组下标0开始将两个数对应位的数字相加再加上前一位的进位初始进位为0处理进位如果当前位的和≥10那么进位设为和÷10当前位保留和%10如果所有位都处理完后仍有进位需将进位作为新的最高位存入结果数组。适用场景超大数求和、斐波那契递推、整数划分DP转移等场景都是高精度加法的常见应用。2.2 完整代码实现C版入门友好这里用vector容器存储数组无需手动管理数组长度更便捷实现两个非负超大数的加法代码包含详细注释新手可直接复制运行#include iostream #include vector #include algorithm // 用于reverse函数 using namespace std; // 高精度加法a b返回结果字符串形式 string add(string a, string b) { // 步骤1将字符串转换为vector数组低位在前 vectorint numA, numB, res; for (int i a.size() - 1; i 0; --i) { numA.push_back(a[i] - 0); // 字符转数字逆序存入 } for (int i b.size() - 1; i 0; --i) { numB.push_back(b[i] - 0); } // 步骤2逐位相加处理进位 int carry 0; // 进位标记初始为0 int i 0; // 遍历两个数组直到所有位都处理完且进位为0 while (i numA.size() || i numB.size() || carry ! 0) { // 取出当前位的数字若数组已遍历完补0 int x i numA.size() ? numA[i] : 0; int y i numB.size() ? numB[i] : 0; // 计算当前位的和 甲数字当前位 乙数字当前位 进位 int sum x y carry; res.push_back(sum % 10); // 保留当前位个位 carry sum / 10; // 更新进位 i; } // 步骤3将结果数组逆序转换为字符串 string result; for (int i res.size() - 1; i 0; --i) { result (res[i] 0); // 数字转字符 } return result; } int main() { // 测试案例 string a, b; cout 请输入第一个非负超大数; cin a; cout 请输入第二个非负超大数; cin b; string sum add(a, b); cout 两数之和 sum endl; return 0; }2.3 关键注意点避坑必看适用场景超大数求差、大数比较、区间范围计算等场景。3.2 完整代码实现C版兼容正负代码包含“大小比较”“借位处理”“前导零去除”三个核心模块注释详细可直接复用进位处理一定要记得处理“最高位进位”比如999 1 1000若不处理进位结果会是000遗漏最高位的1数组补0当两个数字位数不同时短的数组后续位补0避免数组越界输入输出始终用字符串接收输入、输出结果避免直接用数字类型导致溢出。三、高精度减法逐位相减处理借位3.1 核心原理复刻竖式减法减法比加法多了两个步骤比较大小和处理借位以5231 - 1789为例5231 - 1789 ------ 3442核心步骤拆解比较大小先判断被减数a是否大于等于减数b。如果a b结果为负数此时需要交换a和b计算b - a最后在结果前加负号对齐同样将两个数字的最低位对齐数组下标0逐位相减从最低位开始用a的当前位减去b的当前位再减去前一位的借位初始借位为0处理借位如果当前位相减后小于0说明需要向高位借位此时当前位加10借位设为1若相减后大于等于0借位设为0去除前导零运算完成后结果数组可能存在前导零比如1000 - 999 0001需要去除只保留有效位数注意结果为0时需保留一个0。#include iostream #include vector #include algorithm using namespace std; // 辅助函数比较两个非负数字符串的大小返回true表示a b bool compare(string a, string b) { // 先比较长度长度长的数字更大 if (a.size() ! b.size()) { return a.size() b.size(); } // 长度相同逐位比较从高位到低位 for (int i 0; i a.size(); i) { if (a[i] ! b[i]) { return a[i] b[i]; } } return true; // 两数相等 } // 高精度减法a - b保证a b返回非负结果字符串 string subtractCore(string a, string b) { // 步骤1字符串转数组低位在前 vectorint numA, numB, res; for (int i a.size() - 1; i 0; --i) { numA.push_back(a[i] - 0); } for (int i b.size() - 1; i 0; --i) { numB.push_back(b[i] - 0); } // 步骤2逐位相减处理借位 int borrow 0; // 借位标记初始为0 int i 0; while (i numA.size()) { // 取出当前位数字b遍历完则补0 int x numA[i]; int y i numB.size() ? numB[i] : 0; // 计算当前位差值 甲当前位 - 乙当前位 - 借位 int diff x - y - borrow; if (diff 0) { // 需要借位 diff 10; borrow 1; } else { // 无需借位 borrow 0; } res.push_back(diff); i; } // 步骤3去除前导零保留至少一个0 while (res.size() 1 res.back() 0) { res.pop_back(); } // 步骤4数组逆序转字符串 string result; for (int i res.size() - 1; i 0; --i) { result (res[i] 0); } return result; } // 高精度减法主函数处理正负情况 string subtract(string a, string b) { if (compare(a, b)) { // a b直接计算a - b return subtractCore(a, b); } else { // a b结果为负计算b - a后加负号 return - subtractCore(b, a); } } int main() { // 测试案例 string a, b; cout 请输入被减数超大数; cin a; cout 请输入减数超大数; cin b; string diff subtract(a, b); cout 两数之差 diff endl; return 0; }3.3 关键注意点避坑必看大小比较必须先判断被减数和减数的大小否则会出现负数结果遗漏负号的问题借位处理借位后高位数字会减少1后续计算时必须考虑这个借位避免漏算前导零去除前导零时要保证结果至少保留一个0比如0 - 0 0不能返回空字符串正负处理只有当a b时结果才加负号其他情况直接返回非负结果。

更多文章