高精度
高精度
对于一些比较庞大的数据量来说,变量是无法储存的,这是就会发生溢出,像我们常用的基本数据数据类型,它们存储数据的大小是有限的。
- 整型
- bool : 其值为 true (非0值,常解释为 1) 或 false (值为0)
- char : 通常占1个字节,取值范围是 \(-127\) ~ \(128\)
- signed char : 取值范围是 \(-127\) ~ \(128\)
- unsigned char :取值范围是 \(0\) ~ \(255\)
- short : 通常占2各字节,取值范围是 \(-2^{15}\) ~ \(2^{15} - 1\)
- unsigned short :取值范围是 \(0\) ~ \(2^{16} - 1\)
- int : 通常占4字节,取值范围是 \(-2^{31}\) ~ \(2^{31}-1\)
- unsigned int: 取值范围是 \(0\) ~ \(2^{32} - 1\)
- long : 通常占4个字节 取值范围是 \(-2^{31}\) ~ \(2^{31}-1\)
- unsigned long : 取值范围是 \(0\) ~ \(2^{32} - 1\)
- long long : 通常占8个字节, 取值范围是 \(-2^{63}\) ~ \(2^{63} - 1\)
- unsigned long long : 取值范围是 \(0\) ~ \(2^{64} - 1\)
PS:C++规定,int 至少和 short 一样大,long 至少和 int 一样大,long long 至少和 long 一样大,其中,long long 是在 C++11 中新定义的。
- 浮点类型:
- float : 通常使用32位内存 ,精度为6~7位有效数字,范围为\(-2^{128}\) ~ \(+2^{128}\),也即-3.40E+38 ~ +3.40E+38。
- double: 通常使用 64 为内存,精度为15~16位,范围为\(-2^{1024}\) ~ \(+2^{1024}\),也即-1.79E+308 ~ +1.79E+308。
- long double : 通常使用 80,96,128 位内存 精度为18~19
这个时候,就可以用数组,字符串来储存数据的每一位了,通常高精度有四种常见场景。
高精度加法
对于两个数加法的高精度处理,相关的例题有P1601 A+B Problem(高精) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)思路就是用字符串将每位数储存,逐个相加,如果结果大于10,就要进位,将计算结果按位储存在数组中,再循环输出,代码可能显得比较臃肿,但好在思路清晰,望见谅。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
//输入两个加数
string a, b;
cin >> a >> b;
//特殊情况处理
if (a == "0" && b == "0") {
cout << "0";
return 0;
}
//确定大小
int L1 = a.size();
int L2 = b.size();
int L3 = L1 > L2 ? L1 : L2;
//构造数组
vector<int> va(L3 + 1, 0);
vector<int> vb(L3 + 1, 0);
//将每一位储存
for (int i = L3, j = L1 - 1; j >= 0; i--, j--) {
va[i] = a[j] - '0';
}
for (int i = L3, j = L2 - 1; j >= 0; i--, j--) {
vb[i] = b[j] - '0';
}
//储存余数
int x = 0;
//逐个位数相加
for (int i = L3; i >= 0; i--) {
int temp = va[i];
va[i] = (vb[i] + va[i] + x) % 10;
//进位处理
x = (vb[i] + temp + x) / 10;
}
//处理数组前 "0"
int i;
for (i = 0; i <= L3; i++) {
if (va[i] != 0) {
break;
} }
//输出结果
for (int j = i; j <= L3; j++) {
cout << va[j];
}
return 0;
}
高精度减法
对于高精度减法来说,与高精度加法类似,用字符串处理,需要考虑的要点是需要处理借位,就是被减数当前位小于减数当前位,就需要向前一位借 '1',相关的例题有:P2142 高精度减法 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include <string>
#include <iostream>
using namespace std;
int main() {
bool index = false;
string a, b;
cin >> a >> b;
//判断减数与被减数的大小关系
if (a.size() < b.size()) {
string temp = a;
a = b;
b = temp;
index = true;
} else if (a.size() == b.size()) {
if (a < b) {
string temp = a;
a = b;
b = temp;
index = true;
} }
int L1 = a.size();
int L2 = b.size();
//进行补位,让减数与被减数位数相等
for (int i = L2; i < L1; i++) {
b = '0' + b;
}
int x = 0;
for (int i = L1 - 1; i >= 0; i--) {
int y = (a[i] - '0') - x - (b[i] - '0');
x = 0;
//借位处理
if (y < 0) {
y += 10;
x = 1;
}
a[i] = char(y + '0');
}
//输出从非 '0' 开始
int i = 0;
for (; i < L1; i++) {
if (a[i] != '0') {
break;
} if (i == L1 - 1) {
cout << 0;
return 0;
} }
//是否输出 '-' 号
if (index) {
cout << '-';
}
//输出每一位
for (int j = i; j < L1; j++) {
cout << a[j];
} return 0;
}
高精度乘法
乘法较于加法和减法无疑是麻烦一些,一个乘数的每一位与另个乘数相乘,可以用嵌套循环来解决,乘数固定一个位不变,遍历另一个乘数的每一位与之相乘,这是内循环,一个内循环结束,乘数的位移动,是外循环。相关的题是:P1303 A*B Problem - 洛谷 | 计算机科学教育新生态 (luogu.com.cn),用数组加字符串解决比较方便,思路比较清晰,虽然代码可以优化,但作为初学者,对优化方面没有了解😜。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
string a, b;
cin >> a >> b;
//处理特殊情况
if (a == "0" || b == "0") {
cout << 0;
return 0;
}
//计算长度
int L1 = a.size();
int L2 = b.size();
//转换为数字
vector<int> up(L1, 0);
vector<int> down(L2, 0);
vector<int> res(L1 + L2, 0);
//个位放在尾部
for (int i = 0; i < L1; i++) {
up[L1 - 1 - i] = a[L1 - 1 - i] - '0';
}
for (int i = 0; i < L2; i++) {
down[L2 - 1 - i] = b[L2 - 1 - i] - '0';
}
//乘法处理
for (int i = L2 - 1; i >= 0; i--) { // down
int x = 0;
int j, k = 0;
for (j = L1 - 1; j >= 0; j--, k++) { //up
int temp = res[L1 + i - k]; //为下方计算进位做打算
res[L1 + i - k] = (res[L1 + i - k] + down[i] * up[j] + x) % 10;
x = (temp + down[i] * up[j] + x) / 10;
}
if (x != 0) {
res[i] = x;
}
}
//确定首位非零的位置
int i = 0;
for (i; i < L1 + L2; i++) {
if (res[i] != 0) {
break;
}
}
//输出
for (int j = i; j < L1 + L2; j++) {
cout << res[j];
}
return 0;
}