高精度
2189 字
11 分钟
高精度
概述
当数据的值特别大,超出各种基本数据类型的存储范围时,需要使用高精度算法来计算加减乘除。
基本思想:
- 先用字符串读入这个数
- 用数组逆序存储该数的每一位
- 利用数组,模拟加减乘除运算的过程
高精度算法本质上还是模拟算法,用代码模拟小学列竖式计算加减乘除的过程。
高精度加法
题目:a+b问题,不考虑负数 数据范围:a,b<=10^500
算法思路
- 用字符串读入数据
- 将字符串的每一份拆分,逆序放在数组中
- 模拟列竖式计算过程:
- 对应位累加
- 处理进位
- 处理余数
- 处理结果位数
代码实现
#include <iostream>using namespace std;
const int N = 1e6 + 10; // 定义足够大的数组大小int a[N], b[N], c[N]; // a, b存储输入数字,c存储结果int la, lb, lc; // 分别表示a, b, c的长度
// 高精度加法模板 - c = a + bvoid 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; // 处理余数:保留当前位的个位数 } // 处理最高位的进位 if(c[lc]) lc++; // 如果最高位有进位,结果长度加1}
int main() { string x, y; cin >> x >> y; // 用字符串读入两个大数
// 1. 拆分每一位,逆序放在数组中 la = x.size(); // 获取第一个数字的长度 lb = y.size(); // 获取第二个数字的长度 lc = max(la, lb); // 结果长度至少是两个数字中较长的那个
// 将字符串转换为数字数组(逆序存储,个位在索引0) 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'; // 逆序存储,方便计算
// 2. 模拟加法的过程 add(c, a, b); // 调用高精度加法函数:c = a + b
// 输出结果(需要逆序输出,因为存储时是逆序的) for(int i = lc - 1; i >= 0; i--) cout << c[i];
return 0;}高精度减法
描述:高精度减法,第二个数可能比第一个数大
算法思路
模拟小学「列竖式」计算「两数相减」的过程:
- 用字符串读入数据
- 判断两个数的大小,让较大的数在前 · 位数相等:按字典序比较 · 位数不等:按照字符串的长度比较
- 将字符串的每一位拆分,逆序放数组中
- 模拟列竖式计算的过程: · 对应位求差 · 处理借位
- 处理前导零
代码示例
#include <iostream>using namespace std;
const int N = 1e6 + 10; // 定义足够大的数组大小int a[N], b[N], c[N]; // a, b存储输入数字,c存储结果int la, lb, lc; // 分别表示a, b, c的长度
// 比较两个字符串表示的数字大小bool cmp(string& x, string& y) { // 先比较长度:长度不同时,长度小的数字小 if(x.size() != y.size()) return x.size() < y.size(); // 长度相同时,按照字典序比较 return x < y;}
// 高精度减法模板 - c = a - bvoid 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; // 向高位借1 c[i] += 10; // 当前位加10 } } // 处理前导零:去掉结果开头不必要的0 while(lc > 1 && c[lc - 1] == 0) lc--;}
int main() { string x, y; cin >> x >> y; // 用字符串读入两个大数
// 比较大小,确保用较大的数减较小的数 if(cmp(x, y)) { swap(x, y); // 如果x < y,交换两个数 cout << '-'; // 输出负号 }
// 1. 拆分每一位,逆序放在数组中 la = x.size(); // 获取第一个数字的长度 lb = y.size(); // 获取第二个数字的长度 lc = max(la, lb); // 结果长度至少是两个数字中较长的那个
// 将字符串转换为数字数组(逆序存储,个位在索引0) for(int i = 0; i < la; i++) a[la - i - 1] = x[i] - '0'; for(int i = 0; i < lb; i++) b[lb - i - 1] = y[i] - '0';
// 2. 模拟减法的过程 sub(c, a, b); // 调用高精度减法函数:c = a - b
// 输出结果(需要逆序输出,因为存储时是逆序的) for(int i = lc - 1; i >= 0; i--) cout << c[i];
return 0;}高精度乘法
算法思路
采用无进位相乘再相加的方法
- 用字符串读入数据
- 将字符串的每一位拆分,逆序放在数组中
- 模拟无进位相乘再相加的过程: · 对应位求乘积 · 乘完之后处理进位 · 处理余数
- 处理前导零
代码实现
#include <iostream>using namespace std;
const int N = 1e6 + 10; // 定义足够大的数组大小int a[N], b[N], c[N]; // a, b存储输入数字,c存储结果int la, lb, lc; // 分别表示a, b, c的长度
// 高精度乘法模板 - c = a * bvoid mul(int c[], int a[], int b[]) { // 无进位相乘,然后相加 for(int i = 0; i < la; i++) { for(int j = 0; j < lb; j++) { // a的第i位与b的第j位相乘,结果加到c的第i+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 while(lc > 1 && c[lc - 1] == 0) lc--;}
int main() { string x, y; cin >> x >> y; // 用字符串读入两个大数
// 1. 拆分每一位,逆序放在数组中 la = x.size(); // 获取第一个数字的长度 lb = y.size(); // 获取第二个数字的长度 lc = la + lb; // 乘积的最大可能长度是两个数字长度之和
// 将字符串转换为数字数组(逆序存储,个位在索引0) 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';
// 2. 模拟乘法的过程 mul(c, a, b); // 调用高精度乘法函数:c = a * b
// 输出结果(需要逆序输出,因为存储时是逆序的) for(int i = lc - 1; i >= 0; i--) cout << c[i];
return 0;}高精度除法
描述:输入两个整数a,b,输出它们的商(整数部分)
算法思路
- 定义一个指针i从高位遍历被除数
- 一个变量t标记当前[被除的数]
- 记除数是b
- 计算过程:
- 更新当前被除数:t = t* 10 +a[i];
- t/b 表示这一位的商
- t%b 表示这一位的余数
- 用t记录这一次的余数,遍历到下一位时重复过程
- 处理前导0
代码实现
#include <iostream>using namespace std;
const int N = 1e6 + 10; // 定义足够大的数组大小typedef long long LL; // 定义长整型别名int a[N], b, c[N]; // a存储被除数,b存储除数,c存储商int la, lc; // 分别表示a, c的长度
// 高精度除法模板 - c = a / b (高精度 / 低精度)void div(int c[], int a[], int b) { LL t = 0; // 标记每次除完之后的余数
// 从高位到低位遍历被除数 for(int i = la - 1; i >= 0; i--) { // 计算当前的被除数:余数×10 + 当前位数字 t = t * 10 + a[i];
// 计算当前位的商和余数 c[i] = t / b; // 当前位的商 t %= b; // 当前位的余数 }
// 处理前导0:去掉商开头不必要的0 while(lc > 1 && c[lc - 1] == 0) lc--;}
int main() { string x; cin >> x >> b; // 用字符串读入被除数,整数读入除数
la = x.size(); // 获取被除数的长度
// 将字符串转换为数字数组(逆序存储,个位在索引0) for(int i = 0; i < la; i++) a[la - 1 - i] = x[i] - '0';
// 模拟除法的过程 lc = la; // 商的长度初始化为被除数的长度 div(c, a, b); // 调用高精度除法函数:c = a / b
// 输出结果(需要逆序输出,因为存储时是逆序的) for(int i = lc - 1; i >= 0; i--) cout << c[i];
return 0;}文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
相关文章智能推荐
1
算法错题本
算法在一个圆形操场的四周摆放 $N$ 堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的 $2$ 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
2
数论
算法如果 $a$ 除以 $b$ 没有余数,那么 $a$ 就是 $b$ 的倍数,$b$ 就是 $a$ 的约数,记作 $b \mid a$。 约数,也称因数。
3
分治
算法问题一分为二 —递归(DFS)-合并答案 时间优化:O(nn)-O(n log n)
4
贪心
算法思路 猜一个排序依据(比如按重量、按 deadline、按权值差等) 任取相邻两项 i, j 假设顺序错了,写出交换前/后只与这两头有关的代价/收益表达式 令“交换后更优”得不等式,化简成key(i) key(j)这个 key 就是 so…
5
二分
算法解集具“二段性”即可用:前一段不满足,后一段一定满足,或反之
随机文章随机推荐

