完整代码请扒拉到最底下↓。
解题要求:掌握基础的判断、循环、数组、字符串,以及一些计算原理即可。
“高精度”是怎么一回事?
简单来说,就是我们计算的数字太大太大了,导致已经超出了我们可以定义的范围,以至于我们不能用正常的定义来定义和计算数字,不然只能计算我们能所容纳的数字部分,无法达
到要求。
比如longlong可以定义的范围在 -(2的63次方)到 2的63次方-1
如果此时数据过大,超过了longlong可以定义的范围,那么我们该如何计算它呢?这就需要我们运用到高精度的知识了。
题外话:Python没有这个限制,所以一行代码就解决了。
高精度除法计算的原理
1.存储数据
你看既然我们数据太大,普通的定义都装不下了,那我们拿什么来装数据呢?没错,就是
数组
有同学可能会问:我们又不知道这个数有多长,我现在又不会用vector,(其实我现在也还是还是不会),那我们定义数组的时候应该定义多长的数组呢?
根据题意0<a<10的5000次幂,则将被除数和商的数组长度设置为5000(基础的数学运算,可以自己推算一下),而除数0<b<10的9次幂,这里可以用int来实现了。
int dividend[5000] = { 0 };//被除数
int quotient[5000] = {0};//商
//由于int无法容纳足够多的数字,只能用string来存储
string a;
int divisor;//题目中除数范围在int内
cin >> a >> divisor;
2.输入数据
很简单,先获取字符串的长度,然后利用for循环将数字一个一个塞入数组,不过注意由于我们定义的是string,所以存入数组的还是string类型,这里我们就需要进行一个数据类型的转换,把它转换成int,这样才能计算。
//获取字符串的长度:
int al = a.length();
for (int i = 0; i < al; i++)
{
//字符串a是可以变成a[i]的,每一个元素就是对应的字符
//-'0'可以将数字字符转换为数字
dividend[i] = a[i]-'0';
}
3.计算原理(核心)
接下来就是应该如何计算了,这里我们就需要模拟没有计算器的情况下,我们人是如何手算的,于是就需要用到我们在小学学习的竖式运算。
02
--------
5|10
0
-----
10
10
-------
0
1.先用被除数的一位去和除数相除
2.如果这一位比除数小,那么就需要用它减去商和除数的乘积(即0),然后后移补位,再次相除
3.如果这一位比除数大,那么先相除得出商,然后被除数减去商和除数的乘积,然后后移补位,再次计算
4.最终输出商
这里你可以发现两种情况中其实商的计算过程是一模一样的,所以我们可以采用同一个公式来计算商。
然后就是补位怎么补,其实就是最基本的数学运算,前一个数乘10+后一个数乘1,就得到一个二位数了,那如果减下来的数本来就是二位数呢?其实也是一个道理,不信你试试,因为我们补位永远都是只补一位,所以前边的数无论是多大只要乘10,就能为补位腾出一位的空间。
最后我们需要一个临时容器来存储一下中间相减的数,不过注意由于补位需要乘10,所以int无法容纳,需要long long来定义。
代码如下:
long long remainder = 0;//记录中间数
for (int i = 0;i < al;i++)//被除数开始逐个计算
{
quotient[i] = (remainder * 10 + dividend[i]) / divisor;//计算商的每一位
remainder = remainder * 10 + dividend[i] - (quotient[i] * divisor);//计算相减的中间数
}
4.输出数据
我们手动计算的时候是把商里的0自动除掉的,但是电脑计算的时候会保留,所以我们需要一个判断机制,在找到第一个非零数之前,把遇到的0都跳过,这样就能保证输出的时候数字前边不会有0
int zeroflag = 0;//设置一个发现非零数的标记,默认为0
for (int i = 0; i < al ; i++)
{
//若前边几位都是0,且不是最后一位,且标记还是为0,则跳过本次循环
if (quotient[i] == 0 && zeroflag == 0 && i < al-1)
{
continue;
}
zeroflag = 1;//找到第一个非零数之后,就将标记设位1,这样就不会影响到后续的0
cout << quotient[i];
}
最后就是完整代码了
#include <iostream>
using namespace std;
int dividend[5000] = { 0 };//被除数
int quotient[5000] = {0};//商
int main() {
//由于int无法容纳足够多的数字,只能用string来存储
string a;
int divisor;//题目中除数范围在int内
cin >> a >> divisor;
//获取字符串的长度:
int al = a.length();
for (int i = 0; i < al; i++)
{
//字符串a是可以变成a[i]的,每一个元素就是对应的字符
//-'0'可以将数字字符转换为数字
dividend[i] = a[i]-'0';
}
long long remainder = 0;//记录中间数
for (int i = 0;i < al;i++)//被除数开始逐个计算
{
quotient[i] = (remainder * 10 + dividend[i]) / divisor;//计算商的每一位
remainder = remainder * 10 + dividend[i] - (quotient[i] * divisor);//计算相减的中间数
}
//不判断到最后一位的原因是,如果计算结果为0,能保证输出计算结果
int zeroflag = 0;//设置一个发现非零数的标记,默认为0
for (int i = 0; i < al ; i++)
{
//若前边几位都是0,且不是最后一位,且标记还是为0,则跳过本次循环
if (quotient[i] == 0 && zeroflag == 0 && i < al-1)
{
continue;
}
zeroflag = 1;//找到第一个非零数之后,就将标记设位1,这样就不会影响到后续的0
cout << quotient[i];
}
return 0;
}
最后的结语
其实这个并不是真正的高精度除法,因为没有考虑小数和负数,有兴趣的小伙伴可以自己下去尝试一下。
另外这也是高精度加减乘除最后一篇文章了,想了解高精度其他运算方法的可以去看我的博客,里边都写的有哦。