【基础算法】高精度(加、减、乘、除)

article/2025/6/21 12:05:38

文章目录

  • 什么是高精度
  • 1. 高精度加法
    • 解题思路
    • 代码实现
  • 2. 高精度减法
    • 解题思路
    • 代码实现
  • 3. 高精度乘法
    • 解题思路
    • 代码实现
  • 4. 高精度除法 (高精度 / 低精度)
    • 解题思路
    • 代码实现

什么是高精度

我们平时使用加减乘除的时候都是直接使用 + - * / 这些符号,前提是进行运算的数字在一定的范围之内。一旦这个数字非常大的时候,比如 10 10086 10^{10086} 1010086 这个样一个天文数字,普通的 intlong long 这些类型根本容不下它,像用它进行运算就更不可能了,所以这个时候我们就需要用到高精度算法来计算加减乘除。

高精度算法的核心有两步:

  1. 先用字符串读入这个数,然后用数组逆序存储该数的每⼀位;

  2. 利用数组,模拟小学就学过的加减乘除竖式运算过程。


1. 高精度加法

【题目链接】

P1601 A+B Problem(高精) - 洛谷

【题目描述】

高精度加法,相当于 a+b problem,不用考虑负数

【输入格式】

分两行输入。 a , b ≤ 10 500 a,b \leq 10^{500} a,b10500

【输出格式】

输出只有一行,代表 a + b a+b a+b 的值。

【示例一】

输入

1
1

输出

2

【示例二】

输入

1001
9099

输出

10100

【说明/提示】

20 % 20\% 20% 的测试数据, 0 ≤ a , b ≤ 10 9 0\le a,b \le10^9 0a,b109

40 % 40\% 40% 的测试数据, 0 ≤ a , b ≤ 10 18 0\le a,b \le10^{18} 0a,b1018


解题思路

  1. 先用字符串读入这个数,然后用数组逆序存储该数的每⼀位

我们可以在全局上设置三个数组 a[], b[], c[],分别用于存储加法运算的左操作数、右操作数以及左后的结果的每一位(逆序)。比如 456 + 789 = 1245 456 + 789 = 1245 456+789=1245,对应 a[] = {6, 5, 4}, b = {9, 8, 7}, c = {5, 4, 2, 1}。同时,设置三个变量 la, lb, lc 用于记录数字的长度。

const int N = 1e6 + 10;
int a[N], b[N], c[N];
int la, lb, lc;

为什么要逆序?这是因为我们在人为计算竖式的时候是按照从个位到最高位去计算的,相当于逆序计算。当我们逆序存储后之后在模拟竖式计算的过程中就可以顺序遍历数组进行操作。

请添加图片描述

有了这几个变量之后就可以存储数字了,这个过程中,字符串的最后一位应存储在数组的第一位,以此类推。注意字符串中的每一位存储的是字符,而数组中存储的是数,所以在搬运各个位的时候要减去 '0' 转化成对应的数字。

string x, y;
cin >> x >> y;
la = x.size(), lb = y.size(), lc = max(la, lb); // 这里先暂时写成max(la, lb)
for(int i = 0; i < la; ++i) a[la - 1 - i] = x[i] - '0';
for(int i = 0; i < lb; ++i) b[lb - 1 - i] = y[i] - '0';
  1. 利用数组,模拟小学就学过的加减乘除竖式运算过程

用循环遍历数组,填写 c[] 中的每一位。

for(int i = 0; i < lc; ++i)
{c[i] += a[i] + b[i];  // 对应位相加,再加上进位(+=实际就是加上进位)c[i + 1] = c[i] / 10;  // 进到下一位去c[i] %= 10;  // 当前位对应的数
}

代码实现

#include<iostream>using namespace std;const int N = 1e6 + 10;
int a[N], b[N], c[N];
int la, lb, lc;// 高精度加法
void add(int c[], int a[], int b[])
{// 模拟竖式运算for(int i = 0; i < lc; ++i){c[i] += a[i] + b[i];  // 对应位相加,再加上进位c[i + 1] = c[i] / 10;  // 处理进位c[i] %= 10;  // 当前位对应的数}// 如果最后一位进位了,那么c的长度+1if(c[lc]) ++lc;
}int main()
{string x, y;cin >> x >> y;// 拆分每一位数字,并逆序放在数组中la = x.size(), lb = y.size(), lc = max(la, lb);for(int i = 0; i < la; ++i) a[la - 1 - i] = x[i] - '0';for(int i = 0; i < lb; ++i) b[lb - 1 - i] = y[i] - '0';// 高精度加法:c = a + badd(c, a, b);// 逆序输出for(int i = lc - 1; i >= 0; --i) cout << c[i];return 0;
}

2. 高精度减法

【题目链接】

P2142 高精度减法 - 洛谷

【题目描述】

高精度减法。

【输入格式】

两个整数 a , b a,b a,b(第二个可能比第一个大)。

【输出格式】

结果(是负数要输出负号)。

【示例一】

输入

2
1

输出

1

【说明/提示】

  • 20 % 20\% 20% 数据 a , b a,b a,b 在 long long 范围内;
  • 100 % 100\% 100% 数据 0 < a , b ≤ 10 10086 0<a,b\le 10^{10086} 0<a,b1010086

解题思路

和高精度加法很类似,只不过这里要处理两个细节

  1. 结果可能为负数

x - yxy 小的时候,结果为负数,我们可以交换 xy 的值然后在计算的结果后加上 - 即可。

而由于 xy 是字符串,所以判断 xy 的 “大小” 时需要考虑两个点:

  • 字符串长度长的对应的数一定更大。

  • 如果字符串长度一样,那么从前往后比较每一位的字典序(直接用 <> 比较即可)。

  1. 可能出现前导 0

我们最终输出的数的位数由 lc 决定,如果我们让 lc 的值等于 max(la, lb) 而不做处理的话下面的运算结果就为 078 而不是 78

请添加图片描述

所以这个时候我们就需要更新 lc,可以考虑从 c[] 数组的 c[lc - 1] 位置开始往前遍历,直到遇到第一个不是 0 的位置或者lc == 0 的时候结束,在这个过程中不断让 lc 减一。

while(lc > 1 && c[lc - 1] == 0) --lc;  // 处理前导0

注意不能是 lc > 0,因为有可能两数相等相减的最终结果为 0。


代码实现

#include<iostream>using namespace std;const int N = 1e6 + 10;int a[N], b[N], c[N];
int la, lb, lc;// 高精度减法
void sub(int c[], int a[], int b[])
{// 模拟竖式运算for(int i = 0; i < lc; ++i){c[i] += a[i] - b[i];  // 对应位相减,并处理借位if(c[i] < 0){c[i + 1] -= 1;  // 借位c[i] += 10;}}while(lc > 1 && c[lc - 1] == 0) --lc;  // 处理前导0
}int main()
{string x, y;cin >> x >> y;// 计算 x - y// 如果 x 对应的数比 y 对应的数小,那么交换二者,并在结果前加上负号if(x.size() < y.size()|| (x.size() == y.size() && x < y)){cout << '-';swap(x, y);}// 拆分每一位数字,并逆序放在数组中la = x.size(), lb = y.size(), lc = max(la, lb);for(int i = 0; i < la; ++i) a[la - 1 - i] = x[i] - '0';for(int i = 0; i < lb; ++i) b[lb - 1 - i] = y[i] - '0';sub(c, a, b);for(int i = lc - 1; i >= 0; --i) cout << c[i];return 0;
}

3. 高精度乘法

【题目链接】

P1303 A*B Problem - 洛谷

【题目描述】

给出两个非负整数,求它们的乘积。

【输入格式】

输入共两行,每行一个非负整数。

【输出格式】

输出一个非负整数表示乘积。

【示例一】

输入

1 
2

输出

2

【说明/提示】

每个非负整数不超过 10 2000 10^{2000} 102000


解题思路

乘法也可以模拟普通竖式运算过程,但是这样会频繁地处理进位,因为我们也可以采取另一种方式:无进位相乘,相加到对应位上,最后再统一处理进位

请添加图片描述

通过下标的对应关系我们可以发现, a[] 的第 i 个位置的数与 b[] 的第 j 个位置的数相乘恰好就加在 c[] 的第 i + j 位置上。

for(int i = 0; i < la; ++i)for(int j = 0; j < lb; ++j)c[i + j] += a[i] * b[j];

当我们相加之后,再来处理进位。

for(int i = 0; i < lc; ++i)
{c[i + 1] += c[i] / 10;c[i] %= 10;
}

同样可能出现前导 0 的问题,比如 10000 * 0,于是我们采用相同的方法处理:

while(lc > 1 && c[lc - 1] == 0) --lc;

代码实现

#include<iostream>using namespace std;const int N = 1e6 + 10;
int a[N], b[N], c[N];
int la, lb, lc;void mul(int c[], int a[], int b[])
{// 无进位相乘,相加到对应位上for(int i = 0; i < la; ++i)for(int j = 0; j < lb; ++j)c[i + j] += a[i] * b[j];// 处理进位for(int i = 0; i < lc; ++i){c[i + 1] += c[i] / 10;c[i] %= 10;}// 处理前导0while(lc > 1 && c[lc - 1] == 0) --lc;
}int main()
{string x, y;cin >> x >> y;la = x.size(), lb = y.size(), lc = la + lb;  // 两数相乘长度最多不超过la + lbfor(int i = 0; i < la; ++i) a[la - 1 - i] = x[i] - '0';for(int i = 0; i < lb; ++i) b[lb - 1 - i] = y[i] - '0';mul(c, a, b);for(int i = lc - 1; i >= 0; --i) cout << c[i];return 0;
}

4. 高精度除法 (高精度 / 低精度)

【题目链接】

P1480 A/B Problem - 洛谷

【题目描述】

输入两个整数 a , b a,b a,b,输出它们的商。

【输入格式】

两行,第一行是被除数,第二行是除数。

【输出格式】

一行,商的整数部分。

【示例一】

输入

10
2

输出

5

【说明/提示】

0 ≤ a ≤ 10 5000 0\le a\le 10^{5000} 0a105000 1 ≤ b ≤ 10 9 1\le b\le 10^9 1b109


解题思路

由于遇到较多的一般是高精度 / 低精度,所以这里就只讲这种情况。

模拟除法的竖式运算时,实际上会用到多次除法运算,我们可以用一个变量 t 来记录每次的被除数,除出来的商放在 c[] 中的对应位置,然后更新 t,总共除 la 次。最后处理前导 0 即可。

请添加图片描述

注意数据范围,b 最大是 10 9 10^9 109,而 t 可能是 b 的几倍,所以 t 可以用 long long 来存储。


代码实现

#include<iostream>using namespace std;typedef long long LL;const int N = 1e6 + 10;
int a[N], b, c[N];
int la, lc;// 高精度除法(高精度 / 低精度)
void div(int c[], int a[], int b)
{LL t = 0;for(int i = la - 1; i >= 0; --i){t = t * 10 + a[i];  // 计算当前的被除数c[i] = t / b;t %= b;}// 处理前导0while(lc > 1 && c[lc - 1] == 0) --lc;
}int main()
{string x; cin >> x;int b; cin >> b;la = x.size(), lc = la;for(int i = 0; i < la; ++i) a[la - 1 - i] = x[i] - '0'; div(c, a, b);for(int i = lc - 1; i >= 0; --i) cout << c[i];return 0;
}

http://www.hkcw.cn/article/vitmDYdKrK.shtml

相关文章

uni-data-picker级联选择器、fastadmin后端api

记录一个部门及部门人员选择的功能&#xff0c;效果如下&#xff1a; 组件用到了uni-ui的级联选择uni-data-picker 开发文档&#xff1a;uni-app官网 组件要求的数据格式如下&#xff1a; 后端使用的是fastadmin&#xff0c;需要用到fastadmin自带的tree类生成部门树 &#x…

MonitorSDK_性能监控(从Web Vital性能指标、PerformanceObserver API和具体代码实现)

性能监控 性能指标 在实现性能监控前&#xff0c;先了解Web Vitals涉及的常见的性能指标 Web Vitals 是由 Google 推出的网页用户体验衡量指标体系&#xff0c;旨在帮助开发者量化和优化网页在实际用户终端上的性能体验。Web Vitals 强调“以用户为中心”的度量&#xff0c;而…

Kubernetes架构与核心概念深度解析:Pod、Service与RBAC的奥秘

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言&#xff1a;云原生时代的操作系统 在云原生技术浪潮中&#xff0c;Kubernetes&#xff08;简称K8s&#xff09;已成为容器编排领域的"分布式操…

enumiax:IAX 协议用户名枚举器!全参数详细教程!Kali Linux教程!

简介 enumIAX 是一个 Inter Asterisk Exchange 协议用户名暴力枚举器。enumIAX 可以以两种不同的模式运行&#xff1b;顺序用户名猜测或字典攻击。 enumIAX 可以以两种不同的模式运行&#xff1a;顺序用户名猜测或字典攻击。 顺序用户名猜测 在顺序用户名猜测模式下&#xf…

《深入解析SPI协议及其FPGA高效实现》-- 第一篇:SPI协议基础与工作机制

第一篇&#xff1a;SPI协议基础与工作机制 1. 串行外设接口导论 1.1 SPI的核心定位 协议本质 &#xff1a; 全双工同步串行协议&#xff08;对比UART异步、IC半双工&#xff09;核心优势 &#xff1a; 无寻址开销&#xff08;通过片选直连&#xff09;时钟速率可达100MHz&…

C++语法系列之模板进阶

前言 本次会介绍一下非类型模板参数、模板的特化(特例化)和模板的可变参数&#xff0c;不是最开始学的模板 一、非类型模板参数 字面意思,比如&#xff1a; template<size_t N 10> 或者 template<class T,size_t N 10>比如&#xff1a;静态栈就可以用到&#…

STL-list

1.list概述 List 并非 vector 与 string 那样连续的内存空间&#xff0c;list 每次插入或删除一个元素&#xff0c;都会新配置或释放一个元素的空间&#xff0c;所以list对于空间的使用很充分&#xff0c;一点也没有浪费&#xff0c;对于任意位置的插入或删除元素&#xff0c;时…

导入Maven项目

目录 5. 5.1 导入方法1 5.2 导入方法2 5.1 导入方法1 建议选择pom.xml文件导入 导入成功 5.2 导入方法2 导入成功

【含文档+PPT+源码】基于微信小程序的社区便民防诈宣传系统设计与实现

项目介绍 本课程演示的是一款基于微信小程序的社区便民防诈宣传系统设计与实现&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套…

【Unity笔记】Unity WASD+QE 控制角色移动与转向(含 Shift 加速)实现教程

摘要&#xff1a; 在 Unity 游戏开发中&#xff0c;键盘控制角色的移动与转向是基础功能之一。本文详细讲解如何使用 C# 实现基于 WASD 移动、QE 转向 与 Shift 加速奔跑 的角色控制器&#xff0c;适用于第一人称、第三人称、自由漫游等场景。通过直观的 Transform 控制方法与可…

通讯方式学习——单总线协议(2024.04.09)

参考链接1: 单总线器件DS18B20测温程序该怎么编写&#xff1f;这个视进行了详细讲解&#xff01; 在此感谢各位前辈大佬的总结&#xff0c;写这个只是为了记录学习大佬资料的过程&#xff0c;内容基本都是搬运的大佬博客&#xff0c;觉着有用自己搞过来自己记一下&#xff0c;如…

大语言模型(LLM)入门 - (1) 相关概念

文章来自&#xff1a;大语言模型(LLM)小白入门自学项目-TiaoYu-1 GitHub - tiaoyu1122/TiaoYu-1: For People! For Freedom!For People! For Freedom! Contribute to tiaoyu1122/TiaoYu-1 development by creating an account on GitHub.https://github.com/tiaoyu1122/TiaoYu…

[9-3] 串口发送串口发送+接收 江协科技学习笔记(26个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26中断

【Linux系列】Linux/Unix 系统中的 CPU 使用率

博客目录 多核处理器时代的 CPU 使用率计算为什么要这样设计&#xff1f; 解读实际案例&#xff1a;268.76%的 CPU 使用率性能分析的意义 相关工具与监控实践1. top 命令2. htop 命令3. mpstat 命令4. sar 命令 实际应用场景容量规划性能调优故障诊断 深入理解&#xff1a;CPU …

内存管理 : 04段页结合的实际内存管理

一、课程核心主题引入 这一讲&#xff0c;我要给大家讲的是真正的内存管理&#xff0c;也就是段和页结合在一起的内存管理方式。之前提到过&#xff0c;我们先学习了分段管理内存的工作原理&#xff0c;知道操作系统采用分段的方式&#xff0c;让用户程序能以分段的结构进行编…

MCP Python技术实践

目录 1. 引言篇 1.1 什么是MCP&#xff08;Model Context Protocol&#xff09; 1.2 MCP的核心设计理念​ 1.3 MCP的技术背景 1.3 学习目标与内容概览 2. 基础理论篇 2.1 MCP协议架构详解 2.2 MCP消息格式与通信机制 2.3 MCP核心概念深入 3. 环境搭建篇 3.1 开发环境…

《数据结构初阶》【番外篇:二路归并的外排史诗】

【番外篇&#xff1a;多路归并的外排史诗】目录 前言&#xff1a;---------------介绍---------------一、实际情景二、外部排序什么是外部排序&#xff1f; 三、多路归并排序什么是多路归并排序&#xff1f; ---------------实现---------------四、文件归并文件二路归并排序思…

【JavaEE】多线程

5.线程启动&#xff08;statrt方法&#xff09; start方法内部&#xff0c;会调用系统api来在系统内核中创建出线程&#xff0c;创建完线程后会自动调用run方法。 而run方法&#xff0c;就只是描述这个线程要执行的任务。 start 和 run 得区别&#xff08;经典面试题&#xf…

leetcode hot100刷题日记——31.二叉树的直径

二叉树直径详解 题目描述对直径的理解解答&#xff1a;dfs小TIPS 题目描述 对直径的理解 实际上&#xff0c;二叉树的任意一条路径均可以被看作由某个节点为起点&#xff0c;从其左儿子和右儿子向下遍历的路径拼接得到。 那我们找二叉树的直径&#xff08;最大路径&#xff09…

C++ —— B/类与对象(下)

&#x1f308;个人主页&#xff1a;慢了半拍 &#x1f525; 创作专栏&#xff1a;《史上最强算法分析》 | 《无味生》 |《史上最强C语言讲解》 | 《史上最强C练习解析》|《史上最强C讲解》 &#x1f3c6;我的格言&#xff1a;一切只是时间问题。 ​ 目录 一、再谈构造函数 1…