高精度加、减、乘、除及小数详解

高精度

简介

众所周知,在计算机中,每个数据类型都是有存储上限的,那么当数字特别大时应该怎么办呢?这时高精度就产生了。高精度的主要思想就是模拟手算,然后将结果存储到数组中去,相同的,小数也有精度问题,也可以使用相同的思路

存储

这里使用vector 来进行存储,因为这样不需要去管结果有多少位,直接使用push_back() 函数就行了,虽然和数组比起来会慢一些,不过差别也仅仅是常数而已

输入:定义一个字符串,然后将字符串的每位转数字存储起来就行了

string a; vector <int> A; 
cin>>a;
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');

输出:请注意,输入的时候我是反过来的,这样做是为了添加元素比较方便,那么输出的时候也要注意倒着输出

for (int i = C.size() - 1; i >= 0; i--) printf("%d", C[i]);

高精度加法

上文中已经提到,在进行高精度运算是模拟手算的,那么接下来就来回忆一下,我们是怎么手动做加法的
加法的手算模拟
图为用竖式做加法的示例,我们可以发现主要组成部分有两个加数、结果、还有进位,于是我们的变量就可以呼之欲出了

vector <int> A; vector <int> B; vector <int> C; int t;

然后我们再使用循环遍历(从0开始)来进行计算,循环位数较大的那个加数的每一位,然后加到 t t t 里面就行了

那么 C i C_i Ci 就等于 A i + B i A_i+B_i Ai+Bi m o d   10 mod~10 mod 10,进位 t t t 就等于 ( A i + B i ) / 10 (A_i+B_i)/10 (Ai+Bi)/10

注意在循环完之后,如果 t ≠ 0 t\neq 0 t=0 的话,还要加上一位

代码模板

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;

vector<int> add(vector<int>& A,vector<int>& B)
{
	vector<int> C; 
	int t=0;
	for(int i=0;i<max(A.size(),B.size());i++)
	{
		if(i<A.size()) 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;
}
int main()
{
	string a,b;
	cin>>a;
	cin>>b;
	vector<int> A;
	vector<int> B;
	for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
	for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
	vector<int> C=add(A,B);
	for(int i=C.size()-1;i>=0;i--) cout<<C[i];
	return 0;
}

高精度减法

不带负数

首先,先回忆一下我们是怎么用竖式进行减法的
减法的手算模拟
与加法不同,我们在列减法的竖式时会把大数放在上面,小数放在下面,因为减法涉及到借位的问题

所以说在计算机计算的时候,我们要先判断 A A A 是否 ≥ B \geq B B,如果 < B <B <B,为了避免增加代码量我们直接计算 − ( A − B ) -(A-B) (AB) 就行了

那么因为数字特别大,所以我们也需要手写一个比较函数,那么我们是怎么比较两个数的呢?

先比较哪个位数大,位数多的大,如果位数一样,那么分别比较每一位

bool cmp(vector<int>& A, vector<int>& B)
{
	if (A.size() != B.size())return A.size() > B.size();
	for (int i = A.size()-1; i>=0; i--)
	{
		if (A[i] != B[i]) return A[i] > B[i];
	}
	return true;
}

类似加法,变量分别为被减数,减数,结果,借位

使用循环遍历(从0开始)被减数的每一位

借位 t t t = A i − ( B i ) − t A_i-(B_i)-t Ai(Bi)t,如果说最终结果小于0,就要借一位,将 t + 10 t+10 t+10 最后再 m o d   10 mod~10 mod 10 便是 C i C_i Ci

如果借位了 t = 1 t=1 t=1 否则 t = 0 t=0 t=0

注意:为了方便,我们直接不管结果是否小于0,每次都加10,因为如果结果不小于0的话 + 10 +10 +10 m o d   10 mod~10 mod 10 就抵消了对结果不影响

最后还要去除前导0

while (C.size() > 1 && C.back() == 0)
{
	C.pop_back();
}

代码模板

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
bool cmp(vector<int>& A,vector<int>& B)
{
	if(A.size()!=B.size()) return A.size()>B.size();
	for(int i=A.size()-1;i>=0;i--)
	{
		if(A[i]!=B[i]) return A[i]>B[i];
	}
	return true;
}
vector<int> sub(vector<int>& A,vector<int>& B)
{
	int t=0;
	vector<int> C;
	for(int i=0;i<A.size();i++)
	{
		t=A[i]-t;
		if(i<B.size()) t-=B[i];
		C.push_back((t+10)%10);
		if(t<0) t=1;
		else t=0;
	}
	while(C.size()>1&&C.back()==0) C.pop_back();
	return C;
}
int main()
{
	string a,b;
	vector<int> A,B;
	cin>>a;
	cin>>b;
	for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
	for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
	if(cmp(A,B)) 
	{
		vector<int> C=sub(A,B);
		for(int i=C.size()-1;i>=0;i--) cout<<C[i];
	}
	else 
	{
		vector<int> C=sub(B,A);
		cout<<'-';
		for(int i=C.size()-1;i>=0;i--) cout<<C[i];
	}
	return 0;
}

带有负数

  • 自然数 A A A 减负数 B B B 等于 $A+\vert B\vert $
  • 负数 A A A 减自然数 B B B 等于 − ( ∣ A ∣ + B ) -(\vert A \vert+B) (A+B)
  • 自然数 A A A 减负数 B B B 等于 ∣ A ∣ + B \vert A\vert+B A+B
  • 负数 A A A 减负数 B B B ∣ A ∣ ≤ ∣ B ∣ \vert A\vert\leq\vert B\vert AB 时,等于 ∣ B ∣ − ∣ A ∣ \vert B\vert -\vert A\vert BA ∣ A ∣ > ∣ B ∣ \vert A \vert>\vert B\vert A>B 时,等于 − ( ∣ A ∣ − ∣ B ∣ ) -(\vert A\vert-\vert B\vert) (AB)

代码模板

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
bool cmp(vector<int>& A,vector<int>& B)
{
	if(A.size()!=B.size()) return A.size()>B.size();
	for(int i=A.size()-1;i>=0;i--)
	{
		if(A[i]!=B[i]) return A[i]>B[i];
	}
	return true;
}
bool cmp1(vector<int>& A,vector<int>& B)
{
    if(A.size()!=B.size()) return A.size()>B.size();
    for(int i=A.size()-1;i>=0;i--)
	{
		if(A[i]!=B[i]) return A[i]>B[i];
	}
	return false;
}
vector<int> add(vector<int>A,vector<int>B)
{
	vector<int> C; 
	int t=0;
	for(int i=0;i<max(A.size(),B.size());i++)
	{
		if(i<A.size()) 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;
}
vector<int> sub(vector<int>& A,vector<int>& B)
{
	int t=0;
	vector<int> C;
	for(int i=0;i<A.size();i++)
	{
		t=A[i]-t;
		if(i<B.size()) t-=B[i];
		C.push_back((t+10)%10);
		if(t<0) t=1;
		else t=0;
	}
	while(C.size()>1&&C.back()==0) C.pop_back();
	return C;
}
int main()
{
	string a,b;
	vector<int> A,B;
	cin>>a;
	cin>>b;
    if(a[0]!='-'&&b[0]!='-')
    {
        for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
        for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
        if(cmp(A,B)) 
	    {
		    vector<int> C=sub(A,B);
		    for(int i=C.size()-1;i>=0;i--) cout<<C[i];
	    }
	    else 
	    {
		    vector<int> C=sub(B,A);
		    cout<<'-';
		    for(int i=C.size()-1;i>=0;i--) cout<<C[i];
	    }
    }
	else if(a[0]=='-'&&b[0]!='-')
	{
        for(int i=a.size()-1;i>=1;i--) A.push_back(a[i]-'0');
        for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
        vector<int> C=add(A,B);
        cout<<'-';
        for(int i=C.size()-1;i>=0;i--) cout<<C[i];
    }
	else if(a[0]!='-'&&b[0]=='-')
	{
		for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
        for(int i=b.size()-1;i>=1;i--) B.push_back(b[i]-'0');
        vector<int> C=add(A,B);
        for(int i=C.size()-1;i>=0;i--) cout<<C[i];
	}
	else if(a[0]=='-'&&b[0]=='-')
    {
        for(int i=a.size()-1;i>=1;i--) A.push_back(a[i]-'0');
        for(int i=b.size()-1;i>=1;i--) B.push_back(b[i]-'0');
        if(cmp1(A,B))
        {
            cout<<'-';
            vector<int> C=sub(A,B);
            for(int i=C.size()-1;i>=0;i--) cout<<C[i];
        }
        else
        {
            vector<int> C=sub(B,A);
            for(int i=C.size()-1;i>=0;i--) cout<<C[i];
        }
    }
	return 0;
}

高精度乘法

高精度乘低精度

高精度乘法与前面有所不同,在我们用竖式计算乘法时,都是一位对一位的,而在这里因为 b b b 比较小,所以我们直接拿大数的每一位去乘上 b b b 就行了,那么我们还是拿一个竖式举例:
算法过程模拟
先模拟一下这个例子:

C 0 = ( 3 × 12 )   m o d   10 = 6 C_0=(3×12)~mod~10=6 C0=(3×12) mod 10=6
t 1 = 3 × 12 / 10 = 3 t_1=3×12/10=3 t1=3×12/10=3
C 1 = ( 2 × 12 + t 1 )   m o d   10 = 7 C_1=(2×12+t_1)~mod~10=7 C1=(2×12+t1) mod 10=7
t 2 = 2 × 12 / 10 = 2 t_2=2×12/10=2 t2=2×12/10=2
C 2 = ( 1 × 12 + 2 )   m o d   10 = 4 C_2=(1×12+2)~mod~10=4 C2=(1×12+2) mod 10=4
t 3 = 1 × 12 / 10 = 1 t_3=1×12/10=1 t3=1×12/10=1
C 3 = t 3 = 1 C_3=t_3=1 C3=t3=1

那么最终的结果就是 1476

我们用循环遍历 A A A 的每一位就行了

如果最后 t ≠ 0 t\neq0 t=0,那么就添上 t   m o d   10 t~mod~10 t mod 10,注意因为我们是直接乘上 b b b 所以进位不一定是个位数,要用一个循环来做

代码模板

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
vector<int> mul(vector<int>& A,int B)
{
	int t=0;
	vector<int> C;
	for(int i=0;i<A.size();i++)
	{
		t+=A[i]*B;
		C.push_back(t%10);
		t/=10;
	}
	while(t) 
	{
		C.push_back(t%10);
		t/=10;
	}
	return C;
}
int main()
{
	string a;
	int B;
	cin>>a;
	cin>>B;
	vector<int> A;
	for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
	vector<int> C=mul(A,B);
	for(int i=C.size()-1;i>=0;i--) cout<<C[i];
}

高精度乘高精度

因为两个数都很大,所以我们按照手算的方式进行计算,首先先举个例子:
大乘大乘法的手算模拟

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7CPZw5OA-1691065706967)(E:\图片\高精度4.png)]

我们可以发现 A 0 × B 0 A_0×B_0 A0×B0 对应 C 0 C_0 C0 A 1 × B 0 A_1×B_0 A1×B0 对应 C 1 C_1 C1 A 2 × B 0 A_2×B_0 A2×B0 对应 C 2 C_2 C2 以此类推……

我们可以得出 A i × B j A_i×B_j Ai×Bj 对应 C i C_i Ci + _+ + j _j j

那么进位 t t t 就等于 C i C_i Ci + _+ + j _j j / 10 /10 /10

与大数乘小数一样,最后还要添上 t t t,不过这里是模拟手算,不需要再循环了

因为不能直接往后添数,所以我们的答案先初始化长一点,否则无法存储 vector<int> C(A.size() + B.size() + 7, 0);

最后记得去除前导0

代码模板

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
vector<int> mul(vector<int>& A,vector<int>& B)
{
	vector<int> C(A.size()+B.size()+7,0);
	for(int i=0;i<A.size();i++)
	{
		int t=0;
		for(int j=0;j<B.size();j++)
		{
			C[i + j] += A[i] * B[j] + t;
			t = C[i + j] / 10;
			C[i + j] %= 10;
		}
		if(t) C[i+B.size()]+=t;
	}
	while(C.size()>1&&C.back()==0) C.pop_back();
	return C;
}
int main()
{
	string a,b;
	cin>>a; cin>>b;
	vector<int> A,B;
	for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
	for(int i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
	vector<int> C=mul(A,B);
	for(int i=C.size()-1;i>=0;i--) cout<<C[i];
}

注意: t t t 在一轮用完后要及时初始化为0,另外 t t t 一定要赋值为 C i C_i Ci + _+ + j / 10 _j/10 j/10,因为当重叠时可能加法上又有进位。

高精度除法

大数除小数

首先,回想一下我们是怎么用竖式算除法的
除法额手算模拟
我们可以发现几个变量分别是被除数、除数、商、余数

我们使用一个变量 r r r 来存储余数

因为计算机不像人那么聪明,所以一位一位来

首先看1,1除11不够,商0,余1

再看2,因为现在余1,所以变成10+2,12除11够,商1,余1,结束

我们就可以得到 r = r × 10 + A i   m o d   B r=r×10+A_i~mod~B r=r×10+Ai mod B C i = r × 10 + A i   / B C_i=r×10+A_i~/B Ci=r×10+Ai /B

注意因为我们除法是从前往后的,所以去除前导0的时候要先进行翻转

代码模板

#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
vector<int> div(vector<int>& A, int& B,int& r)
{
	r=0; vector<int> C;
	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;
}
int main()
{
	string a;
	cin>>a;
	vector<int> A; int B;
	cin>>B;
	for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
	int r;
	vector<int> C=div(A,B,r);
	for(int i=C.size()-1;i>=0;i--) cout<<C[i];
	cout<<endl;
	cout<<r;
}

压位

因为数组一个位置可以存很大的数,所以我们只存一个数有点浪费,所以我们可以使用压位这个技巧

我们先开两个全局变量 N = x N=x N=x M = 1 0 x M=10^x M=10x (x为你想压的位数)

输入的时候改成

int st=max(0,1-N+1),len=i-st+1;
A.push_back(a.substr(st,len)-'0');

注意所有 / 10   m o d   10 /10~mod~10 /10 mod 10 的操作均要修改成 / M   m o d   M /M~mod~M /M mod M
输出的时候要先输出首位,因为输出的时候有可能不一定正好是 x x x 位数,要使用 printf("%04d",C[i])(以4位举例)
所以当输出完首位后,从C.size()-2开始

高精度小数

保留?位

首先,我们先算出整数部分,因为计算机不像手算,直接算出就可以了,不需要像手算一样一位一位算

然后我们回想一下手算是怎么算小数部分的,我们将上一位算得的余数乘10再除以除数就行了

示意图
小数的手算模拟

代码模板

//c是位数
digit[0]=a/b;
a%=b;
for(int i=1;i<=c+1;i++)
{
	a*=10;
    digit[i]=a/b;
   	a%=b;
}
cout<<digit[0]<<'.';
for(int i=1;i<=c;i++) cout<<digit[i];

四舍五入

由于小数会出现循环,所以题目一般会给出一些要求,例如最后一位四舍五入

四舍五入即为 当数 > = 5 >=5 >=5 进一位, < 5 <5 <5 时舍去

不过我们需要注意,进位的时候可能会出现“多米诺骨牌”

例如 9.99999… 进位后会变成10.00000…,所以我们需要使用循环来处理

注意进位到整数位如果变成10不用管,因为我们是直接输出整数位的

代码模板

if(digit[c+1]>=5) digit[c]++;
for(int i=c;i>0;i--)
{
	if(digit[i]>=10)
    {
       	digit[i]-=10;
        digit[i-1]++;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Problems involving the computation of exact values of very large magnitude and precision are common. For example, the computation of the national debt is a taxing experience for many computer systems. This problem requires that you write a program to compute the exact value of Rn where R is a real number ( 0.0 < R < 99.999 ) and n is an integer such that 0 < n <= 25. 输入说明 The input will consist of a set of pairs of values for R and n. The R value will occupy columns 1 through 6, and the n value will be in columns 8 and 9. 输出说明 The output will consist of one line for each line of input giving the exact value of R^n. Leading zeros should be suppressed in the output. Insignificant trailing zeros must not be printed. Don't print the decimal point if the result is an integer. 输入样例 95.123 12 0.4321 20 5.1234 15 6.7592 9 98.999 10 1.0100 12 输出样例 548815620517731830194541.899025343415715973535967221869852721 .00000005148554641076956121994511276767154838481760200726351203835429763013462401 43992025569.928573701266488041146654993318703707511666295476720493953024 29448126.764121021618164430206909037173276672 90429072743629540498.107596019456651774561044010001 1.126825030131969720661201 小提示 If you don't know how to determine wheather encounted the end of input: s is a string and n is an integer C++ while(cin>>s>>n) { ... } c while(scanf("%s%d",s,&n)==2) //to see if the scanf read in as many items as you want /*while(scanf(%s%d",s,&n)!=EOF) //this also work */ { ... } 来源 East Central North America 1988 北大OJ平台(代理

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值