(二十三)高精度运算

一、前言

这次,高精度帖子,它来了!点个赞
请添加图片描述

二、正文

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 开始计算

计算时我们考虑的情况很多

  1. 不进位,检验是否成功输入以下数据(通过率90%)
输入
12
23
输出
35
  1. 进了位,但没有突破原来的最高位。检验输入以下数据(通过率60%)
输入
16
59
输出
75
  1. 进了位,且突破原来的最高位。检验输入以下数据(通过率20%)
输入
999
111
输出
1110
  1. 结果为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) ab=(ba)
我们可以利用这个公式来解决这个问题

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 计算

计算就不一样了。同样的,把-==搞清楚,不搞清楚很容易错

  1. 不退位,检验是否成功输入以下数据(通过率90%)
输入
90
20
输出
70
  1. 退了位,但>0。检验输入以下数据(通过率60%)
输入
98
89
输出
9
  1. 退了位,且成为负数。检验输入以下数据(通过率60%)
输入
10
12
输出
-2
  1. 结果为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 预览

3.2 一个“简单”的问题

你能结合类(class)和高精度运算完成一个类,要求:

  1. 名字为higpre
  2. 定义方式
    • higpre n; :定义高精度数
    • higpre n(Str); :定义高精度数,并赋值为Str
    • n.init(Str); :赋值为Str
  3. 重载符号+-*/=/前面写higpre类型的数,后面写int类型的数)
  4. 使用higpre::input()higpre::output()进行输入和输出

有兴趣的可以将代码直接发在评论区内(只发这个类的代码)
请添加图片描述

  • 24
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值