当前位置: 首页 > news >正文

高精度加减

一、高精度运算核心前提:理解“数组模拟数位”

在开始具体运算前,我们首先要解决一个关键问题:如何存储超大数?答案很简单——用数组(或容器)拆分存储每一位数字,这里有一个黄金存储规则:低位在前,高位在后

举个例子,数字1234,常规思维是按“高位到低位”存储为[1,2,3,4],但在高精度运算中,我们会存储为[4,3,2,1](数组下标0对应个位,下标1对应十位,下标2对应百位,下标3对应千位)。

为什么要这样存储?原因很简单:加减运算都是从最低位(个位)开始的,低位在前的存储方式,能让我们从数组下标0开始逐位运算,进位、借位时只需操作相邻下标,无需移动整个数组,代码逻辑更简洁,运算效率也更高。后续输出结果时,只需逆序遍历数组即可得到正常的数字顺序。

另外,由于我们通常用字符串输入超大数(直接输入数字会溢出),所以需要先将字符串转换为数组,转换时注意:字符串的第一个字符是数字的最高位,需要逆序存入数组,同时通过字符减去'0'的方式,将字符转换为对应的数字。

二,高精度加法:逐位相加,处理进位

2.1 核心原理(复刻竖式加法)

我们小学学的竖式加法,就是高精度加法的核心逻辑,以1234 + 5678为例:

1234 + 5678 ------ 6912

拆解下来,核心步骤只有3步:

  1. 对齐:将两个数字的最低位对齐(对应数组下标0的位置);

  2. 逐位相加:从最低位(数组下标0)开始,将两个数对应位的数字相加,再加上前一位的进位(初始进位为0);

  3. 处理进位:如果当前位的和≥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数组(低位在前) vector<int> 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:字符串转数组(低位在前) vector<int> 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时,结果才加负号,其他情况直接返回非负结果。

http://www.cnnetsun.cn/news/1914796.html

相关文章:

  • 朱雀AI检测率高怎么降?嘎嘎降AI实操攻略分享
  • uv提供的cpython高版本已经解决了matplotlib无法显示图形问题
  • Batch Normalization在VAE中的花式用法:从防梯度消失到解决posterior collapse的完整指南
  • 告别裸机思维:在IMX6ULL上,用设备树重构你的第一个Linux按键驱动
  • mysql为何建议放弃MyISAM_从InnoDB ACID特性分析
  • 算法基础应用精讲【自动驾驶】-自动驾驶负障碍物感知:从井盖缺失看长尾场景的技术突围
  • Python 切片语法基础:[start:end:step] 全解
  • HALCON copy_obj算子保姆级教程:从‘复制粘贴’到高效数据管理的避坑指南
  • Jellyfin Android TV客户端版本兼容性问题的深度诊断与解决指南
  • 魔兽争霸III现代化改造:5大核心功能让你的经典游戏焕发新生
  • 【硬件开发】自举电路设计实战:从原理到参数计算
  • 计及自适应预测修正的微电网 MPC 优化调度方法研究(Python代码实现)
  • 计算机考研408真题解析(2024-36 VLAN间通信与三层交换机路由机制实战剖析)
  • 救命!收藏这篇就够了, 从ChatGPT到Qwen/GLM,程序员小白大模型入门全攻略(附实战)
  • 从一次应急响应看致远OA wpsAssistServlet漏洞:攻击者如何利用,我们又该如何溯源与加固?
  • MECS UTX1010 461412A02 控制器
  • ncmdumpGUI:3步解锁网易云音乐NCM文件,实现跨平台音乐自由
  • 2025届必备的十大AI学术网站横评
  • 手把手教你用Ansible批量加固CentOS 7/8服务器,一键搞定等保三级合规
  • 有源电力滤波器(APF)模型在Matlab/Simulink中的仿真实践:质量过硬,治理电能质量问题
  • 2026最权威的AI辅助写作助手推荐
  • 企业级 AI Agent Harness Engineering 部署指南
  • 从零到一:在Win11与VS2022上部署OpenSceneGraph 3.6.5的避坑实践
  • 国芯筑基驭智城,第二届酒仙桥论坛解锁“十五五”产城AI增长新范式
  • 【无人机控制】基于LPV方法的无人机模型预测控制器附matlab代码
  • PreScan 8.5.0 与 MATLAB 联调:除了版本,你的编译器设置和启动流程对了吗?
  • 深入理解CUDA内存层次结构:从全局内存到共享内存的优化技巧
  • c语言可否在头文件中定义变量虽有防包含机制但多个源文件包含同一个头文件编译器是每个源文件为单元,当链接器合并的时候会发现相同变量的重复定义报错防包含主要防同一源文件间接包含相同头文件包含A,B。A含B
  • 02 华夏之光永存:(架构师级)昇腾芯片底层架构·达芬奇算力核心道级拆解
  • 从不确定性到生成式对接:DiffDock如何用扩散模型重塑药物发现