文章目录
一、前言
这次,高精度帖子,它来了!点个赞
二、正文
2.1 什么是高精度?
还记得C++的类型吗?int
类型最多可以存储10位数(到十亿),就连long long
类型最多也只能存储20位数(到一千京),因此short
类型的存储最大值可怜得只有5位(到一万),而强大的高精度比long long
类型大好几十倍。因为这种数很大,因此有一种叫法——大数。高精度数没有办法直接加减乘除,应该怎么办呢?
2.2 高精度加
2.2.1 理论
接着我们想一想加法计算原理,这是小学一年级的知识,没有forget吧?
相同数位对齐,从右往左相同数位相加,结果大于10的要进1
在C++数组中,我们刚好可以实现加减。理论成功,实践开始
2.2.2 怎么敲代码?
2.2.2.1 存储两个数
我们可以使用string
类型进行存储
string _a, _b;
cin >> _a >> _b;
2.2.2.2 倒序存入数组
我们都知道,相同数位要对齐,那么如何对齐呢?我们可以换位思考:将两个高精度数字颠倒过来刚好数位对齐。以前讲过,颠倒string
的代码:reverse(str.begin(), str.end());
另外,其实#include <string>
是包含在#include <iostream>
中的,因此我们只需要导入两个库:
#include <iostream> //string包含在里面
#include <algorithm> //别把我forget了,reverse()函数在我里面
#include <cmath> //我要创建大小合适的数组(现在没有写到vector)
因此现在我们需要倒序存入数组,要把ASCII码转换为数字!
建议记住ASCII码中的一些重点字符的编码
字符 | ASCII |
---|---|
'0' | 48 |
'A' | 65 |
'a' | 97 |
reverse(_a.begin(), _a.end());
reverse(_b.begin(), _b.end());
const int MOD = max(_a.length(), _b.length()); //可以使用MOD来代替,简化代码
int a[MOD] = {}; //数组获取越界要乱码
int b[MOD] = {};
int c[MOD+1] = {}; //有可能会在最高位进位,所以要+1
for(int i=0; i<_a.length(); i++)
a[i] = _a[i] - 48; //48是'0'的ASCII码
for(int i=0; i<_b.length(); i++)
b[i] = _b[i] - 48;
2.2.2.3 开始计算
计算时我们考虑的情况很多
- 不进位,检验是否成功输入以下数据(通过率90%)
输入
12
23
输出
35
- 进了位,但没有突破原来的最高位。检验输入以下数据(通过率60%)
输入
16
59
输出
75
- 进了位,且突破原来的最高位。检验输入以下数据(通过率20%)
输入
999
111
输出
1110
- 结果为0的情况(通过率20%)
输入
99
99
输出
0
计算不难,难在细节。应该用=
还是+=
号要想清楚,不然很容易就WA
for(int i=0; i<MOD; i++) {
c[i] += a[i]+b[i]; //用+=才能接受进位
c[i+1] = c[i]/10; //用=或+=都行,因为进位前这些数全是0
c[i] %= 10; //用%=没有理由
}
2.2.2.4 结果更改
我们计算后最终的前导0都要删,但加法一个前导0都没有,为什么呢?别忘了刚才的MOD+1
,为了情况3
reverse(c, c+MOD+1);
string _c;
bool flag = true;
for(int i=0; i<=MOD; i++) {
if(!(c[i]==0&&flag)) {
flag = false;
_c += c[i]+48;
}
}
if(_c.empty()) _c = "0";
cout << _c;
2.2.2.5 最终代码
#include <iostream> //你写#include <bits/stdc++.h> 没有人拦着你
#include <algorithm>
#include <cmath>
using namespace std;
int main() {
string _a, _b;
cin >> _a >> _b;
reverse(_a.begin(), _a.end());
reverse(_b.begin(), _b.end());
const int MOD = max(_a.length(), _b.length()); //可以使用MOD来代替
int a[MOD] = {}; //数组获取越界要乱码
int b[MOD] = {};
int c[MOD+1] = {}; //有可能会在最高位进位,所以要+1
for(int i=0; i<_a.length(); i++)
a[i] = _a[i] - 48; //48是'0'的ASCII码
for(int i=0; i<_b.length(); i++)
b[i] = _b[i] - 48;
for(int i=0; i<MOD; i++) {
c[i] += a[i]+b[i];
c[i+1] = c[i]/10;
c[i] %= 10;
}
reverse(c, c+MOD+1);
string _c;
bool flag = true;
for(int i=0; i<=MOD; i++) {
if(!(c[i]==0&&flag)) {
flag = false;
_c += c[i]+48;
}
}
if(_c.empty()) _c = "0";
cout << _c;
return 0;
}
2.3 高精度减
2.3.1 理论
就是减法竖式
可是在这里有正数减正数等于负数的情况,怎么办呢?
a
−
b
=
−
(
b
−
a
)
a-b=-(b-a)
a−b=−(b−a)
我们可以利用这个公式来解决这个问题
2.3.2 怎么敲代码?
2.3.2.1 存储
和加法一样的,但是要将两个结果为负数的数调换,为了判断两个数哪一个大,我们要这么写
string _a, _b, _c;
cin >> _a >> _b;
if(_a.length()<_b.length()) {
_c += "-";
} else if(_a.length()==_b.length()) {
for(int i=0; i<_a.length(); i++) {
if(_a[i]<_b[i]) {
swap(_a, _b);
_c += "-";
break;
} else if(_a[i]>_b[i]) {
break;
}
}
}
2.3.2.2 倒序存入数组
和加法是一样的
2.3.2.3 计算
计算就不一样了。同样的,把-=
和=
搞清楚,不搞清楚很容易错
- 不退位,检验是否成功输入以下数据(通过率90%)
输入
90
20
输出
70
- 退了位,但>0。检验输入以下数据(通过率60%)
输入
98
89
输出
9
- 退了位,且成为负数。检验输入以下数据(通过率60%)
输入
10
12
输出
-2
- 结果为0的情况(通过率20%)
输入
99
99
输出
0
for(int i=0; i<MOD; i++) {
c[i] += a[i] - b[i];
if(c[i]<0) {
c[i] += 10;
c[i+1] -= 1;
}
}
2.3.2.4 结果更改
一样的
2.3.2.5 全部代码
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
int main() {
string _a, _b, _c;
cin >> _a >> _b;
if(_a.length()<_b.length()) {
_c += "-";
} else if(_a.length()==_b.length()) {
for(int i=0; i<_a.length(); i++) {
if(_a[i]<_b[i]) {
swap(_a, _b);
_c += "-";
break;
} else if(_a[i]>_b[i]) {
break;
}
}
}
reverse(_a.begin(), _a.end());
reverse(_b.begin(), _b.end());
const int MOD = max(_a.length(), _b.length());
int a[MOD] = {};
int b[MOD] = {};
int c[MOD+1] = {};
for(int i=0; i<_a.length(); i++)
a[i] = _a[i] - 48;
for(int i=0; i<_b.length(); i++)
b[i] = _b[i] - 48;
for(int i=0; i<MOD; i++) {
c[i] += a[i] - b[i];
if(c[i]<0) {
c[i] += 10;
c[i+1] -= 1;
}
}
reverse(c, c+MOD+1);
bool flag = true;
for(int i=0; i<=MOD; i++) {
if(!(c[i]==0&&flag)) {
flag = false;
_c += c[i]+48;
}
}
if(_c.empty()) _c = "0";
cout << _c;
return 0;
}
2.3 高精度乘
2.3.1 理论
乘法竖式记得吧,但是这个式子你们知不知道(A和B是两个因数,C是积)
A
i
+
B
j
=
C
i
+
j
A_{i} + B_{j} = C_{i+j}
Ai+Bj=Ci+j
2.3.2 代码
#include <bits/stdc++.h>
using namespace std;
int main() {
string as, bs; //原来的两个字符串
cin >> as >> bs;
int lena = as.size();
int lenb = bs.size();
int len = lena + lenb;
int a[lena]={}, b[lenb]={}, c[len]={};
for(int i=0; i<lena; i++) a[lena-i-1] = as[i] - 48; //倒序存入数组中
for(int i=0; i<lenb; i++) b[lenb-i-1] = bs[i] - 48;
for(int i=0; i<lena; i++) {
int x = 0; //x:进位
for(int j=0; j<lenb; j++) {
c[i+j] += a[i] * b[j] + x;
x = c[i+j] / 10;
c[i+j] %= 10;
}
c[i+lenb] = x;
}
while(c[len-1]==0&&len>1) len --; //删除前导0且除去结果为0的情况
for(int i=len-1; i>=0; i--) cout << c[i];
return 0;
}
2.4 高精度除以低精度
2.4.1 理论
除法竖式懂吧
2.4.2 代码
#include <bits/stdc++.h>
using namespace std;
int main() {
string a1;
int b;
cin >> a1 >> b;
int lena = a1.size();
int a[lena+1] = {}, c[lena+1] = {};
for(int i=0; i<lena; i++)
a[i+1] = a1[i] - 48;
int x = 0; //余数
for(int i=1; i<=lena; i++) {
int t = 10*x+a[i];
c[i] = t/b;
x = t%b;
}
int lenc = 1;
while(c[lenc]==0 && lenc<lena) lenc++;
for(int i=lenc; i<=lena; i++)
cout << c[i];
return 0;
}
三、结语
3.1 预览
- 十九:指针与迭代器
- 二十:位运算与进制
- 二十一:联合体(union)
- 二十二:类(class)
- 二十三:高精度运算
- 二十四:算法进阶
- 二十五:递归
- 二十六:
vector
容器 - 二十七:递推
- 二十八:
set
容器
…
3.2 一个“简单”的问题
你能结合类(class)和高精度运算完成一个类,要求:
- 名字为
higpre
- 定义方式
higpre n;
:定义高精度数higpre n(Str);
:定义高精度数,并赋值为Strn.init(Str);
:赋值为Str
- 重载符号
+
、-
、*
、/
和=
(/
前面写higpre
类型的数,后面写int
类型的数) - 使用
higpre::input()
和higpre::output()
进行输入和输出
有兴趣的可以将代码直接发在评论区内(只发这个类的代码)