全文目录
- 前言
- 高精度加法
- 操作步骤
- 代码模板
- 高精度减法
- 操作步骤
- 代码模板
- 高精度乘法
- 操作步骤
- 代码模板
- 高精度除法
- 操作步骤
- 代码模板
在实际应用中,语言提供的数据类型的最大值或最小值可能不足以支撑我们所进行的运算,这时会导致数据的溢出,所以我们需要一种算法来保证运算结果的精度。高精度运算就是为了解决这一问题而来的。简单来说,就是将数据的每一位的分开,存放在数组中,通过对数组中的每个位置来进行相应的运算来的得到最终结果。
需要注意的是:
1、由于数据过大,所以在进行输入的时候一般用字符串来进行存放
2、在将数据的每一位放进数组中时,最好是反着存放,也就是从前往后,位数依次升高。
这样存放的好处在于方便进位,当他们需要进位的时,可以直接尾插。如果是正着存放的话每次进位都需要头插,全部数据都要往后挪,过于麻烦。
两数相加,数据的前后顺序不影响结果。
但是根据手算,如果是小数加大数的话,不方便进位,所以在进行运算前先保证大数在前,小数在后。方便进行进位运算
高精度加法的步骤:
a + b
1、将
a 和 b
每一位的数据存放分开在数组A 和 B
中(A.size >= B.size
)
2、用
t
来保存上一位进位的结果
3、从前往后,同时遍历
A 和 B
,t
分别加上两个数据的低位A[i] 和 B[i]
,得到这一位的运算结果
4、将这一位的结果的个位尾插进数组中保存起来(一次只能去一位数) ———> (
t % 10
)
5、记录这一位的进位结果 ———> (
t / 10
)
6、重复上述步骤,直到
A
遍历完(因为A.size >= B.size
,所以A
结束了,运算也就结束了)
7、最后一次运算可能还需要进位,将
t
中保留的进位结果尾插进数组
A
+ B
—————————
C
// C = A + B, A >= 0, B >= 0, A.size >= B.size
vector<int> add(vector<int> &A, vector<int> &B)
{
if (A.size() < B.size()) // 保证位数大的 + 位数小的
return add(B, A);
vector<int> C; // 保存运算结果
int t = 0; // 保存上一次的进位结果
for (int i = 0; i < A.size(); i ++)
{
t += A[i]; // 依次相加
if (i < B.size()) // 防止越界
t += B[i];
C.push_back(t % 10); // 取余数保存起来
t /= 10; // 保留进位
}
if (t) // 最后可能还会有进位
C.push_back(t);
return C;
}
两数相减,数据的前后顺序会影响结果的正负性,但是结果的绝对值都是一样的。
但是小数减大数的话,借位不方便操作。所以我们在进行减法前先保证大数在前,小数在后。如果输入的是小数减大数的话提前输出 -
。
高精度减法的步骤:
a - b
1、将
a b
中的每一位的数据存放分开在数组A 和 B
中
2、如果
b > a
提前输出-
,进行b - a
3、用
t
来保存上一位借位的结果
4、从前往后,同时遍历
A 和 B
,A[i]
分别减去t
和B[i]
,得到这一位的运算结果
5、将这一位的结果的个位尾插进数组中保存起来 ———> (
(t + 10) % 10
)
因为t
有可能是负数,需要取10 + t
,t
为正数时(t + 10) % 10 == t % 10
,所以t
需要先加上 10,再进行取模
6、如果
t
为负数,将t
置为 1 来标记这一位的借位情况
7、重复上述步骤,直到
A
遍历完(因为a >= b
,所以A
结束了,运算也就结束了)
8、循环结束后,以
156 - 150
为例,结果可能会存在前导0,所以需要进行前导0 的去除
// 在进行减法之前需要先判断A >= B,如果A < b,先输出'-',然后进行B - A
// 可能会有前导0
bool cmp(vector<int>& a, vector<int>& b)
{
// 如果长度不相等的话就可以立马得出结果
if (a.size() != b.size())
return a.size() > b.size();
// a和b的长度相等的情况,因为数据是反着存放的,所以也要反着比较
for (int i = a.size(); i >= 0; i--)
{
if (a[i] != b[i])
return a[i] >= b[i];
}
// a == b的情况
return true;
}
A
- B
—————————
C
// C = A - B, 满足A >= B, A >= 0, B >= 0
vector<int> sub(vector<int> &A, vector<int> &B)
{
vector<int> C;
for (int i = 0, t = 0; i < A.size(); i ++ )
{
t = A[i] - t; // 向A[i] 借位
if (i < B.size()) // 减后的值
t -= B[i];
C.push_back((t + 10) % 10); // 负数要取10-t的结果,正数的话还是原来的数据
if (t < 0) // 如果现在的值是负数,标记t
t = 1;
else
t = 0;
}
// 去除前导0
while (C.size() > 1 && C.back() == 0)
C.pop_back();
return C;
}
这里以一个大数 a
乘以一个小数 b
为例。
高精度乘法的步骤:
a * b
1、将
a
中的每一位的数据存放分开在数组A
中
2、用
t
来保存上一位的结果
3、从前往后,遍历
A
,A[i]
乘以b
再加上t
,得到这一位的运算结果
4、将这一位的结果的个位尾插进数组中保存起来 ———> (
t % 10
)
5、保留需要进位的数据(
t /= 10
)
6、重复上述步骤,直到
A
遍历完,并且t
为0时结束。
7、循环结束后,以
1547 * 0
为例,结果可能会存在前导0,所以需要进行前导0 的去除
A
* B
—————————
C
// 可能出现大数乘以0的情况
// C = A * b, A >= 0, b >= 0
vector<int> mul(vector<int> &A, int b)
{
vector<int> C;
int t = 0;
for (int i = 0; i < A.size() || t; i ++ ) // t中可能还有数据,需要依次入数组
{
if (i < A.size())
t += A[i] * b; // 得到本位的结果
C.push_back(t % 10);
t /= 10; // 保留需要进位的数据
}
// 去除前导0,可能会有*0的情况
while (C.size() > 1 && C.back() == 0)
C.pop_back();
return C;
}
这里以大数 a
除以 小数 b
为例。
因为除法是从高位开始运算的,所以得到的结果在入数组的时候也是从高位开始入的。为了跟前面的运算保持一致,所以需要在最后逆置数组。因为是一个位一个位进行运算,所以 a
中前几位可能除不尽 b
,所以需要去除前导0。
高精度除法的步骤:
a / b
1、将
a
中的每一位的数据存放分开在数组A
中
2、用
r
来保存上一位的余数,
3、从前往后,遍历
A
,r * 10 + A[i]
,得到这一位的除数
4、将这一位的结果尾插进数组中保存起来 ———> (
r / b
)
5、保留余数(
r %= b
)
6、重复上述步骤,直到
A
遍历完(剩下的余数不用管)
7、循环结束后,逆置数组。以
18954 / 88
为例,结果可能会存在前导0,所以需要进行前导0 的去除
C
————
b| a
// 因为除法是从高位开始运算的,所以入数组的时候也是从高位开始入,在最后需要逆置数组,
// 前几位可能比b小,除不尽b,需要去除前导0
// A / b = C ... r, A >= 0, b > 0
vector<int> div(vector<int> &A, int b, int &r)
{
vector<int> C;
r = 0;
for (int i = A.size() - 1; i >= 0; i -- )
{
r = r * 10 + A[i];
C.push_back(r / b);
r %= b;
}
reverse(C.begin(), C.end());
while (C.size() > 1 && C.back() == 0)
C.pop_back();
return C;
}