高精度

2189 字
11 分钟
高精度

概述#

当数据的值特别大,超出各种基本数据类型的存储范围时,需要使用高精度算法来计算加减乘除。

基本思想:

  1. 先用字符串读入这个数
  2. 用数组逆序存储该数的每一位
  3. 利用数组,模拟加减乘除运算的过程

高精度算法本质上还是模拟算法,用代码模拟小学列竖式计算加减乘除的过程。

高精度加法#

题目:a+b问题,不考虑负数 数据范围:a,b<=10^500

算法思路#

  1. 用字符串读入数据
  2. 将字符串的每一份拆分,逆序放在数组中
  3. 模拟列竖式计算过程:
  • 对应位累加
  • 处理进位
  • 处理余数
  1. 处理结果位数

代码实现#

#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 + b
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; // 处理余数:保留当前位的个位数
}
// 处理最高位的进位
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;
}

高精度减法#

描述:高精度减法,第二个数可能比第一个数大

算法思路#

模拟小学「列竖式」计算「两数相减」的过程:

  1. 用字符串读入数据
  2. 判断两个数的大小,让较大的数在前 · 位数相等:按字典序比较 · 位数不等:按照字符串的长度比较
  3. 将字符串的每一位拆分,逆序放数组中
  4. 模拟列竖式计算的过程: · 对应位求差 · 处理借位
  5. 处理前导零

代码示例#

#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 - b
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; // 向高位借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;
}

高精度乘法#

算法思路#

采用无进位相乘再相加的方法

  1. 用字符串读入数据
  2. 将字符串的每一位拆分,逆序放在数组中
  3. 模拟无进位相乘再相加的过程: · 对应位求乘积 · 乘完之后处理进位 · 处理余数
  4. 处理前导零

代码实现#

#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 * b
void 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,输出它们的商(整数部分)

算法思路#

  1. 定义一个指针i从高位遍历被除数
  2. 一个变量t标记当前[被除的数]
  3. 记除数是b
  4. 计算过程:
  • 更新当前被除数:t = t* 10 +a[i];
  • t/b 表示这一位的商
  • t%b 表示这一位的余数
  • 用t记录这一次的余数,遍历到下一位时重复过程
  1. 处理前导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;
}

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Profile Image of the Author
HandlerOne
おかえり
公告
本站已整理 138 篇技术笔记,并按 8 条主线排成学习路线(141 站);站内搜索与桌宠对话均已上线。
分类
标签
最新动态
站点统计
文章
138
分类
12
标签
42
总字数
597,044
站点信息
构建平台
腾讯云 · Nginx
博客版本
v6.16.8
文章目录