前言
在C++中类型定义的话,对于数据是有较严格要求的。
int类型 在内存中的大小为4byte 范围是-231~231-1
long long 类型 在内存中大小为 8byte 范围是 -263-263-1
简单解释一下啊:
计算机在存储时使用的最小单位是"位",一位存储的是0或是1,一位就是bit。也就是二进制存储。
还有一个概念叫“字节”(byte) 1byte=8bit。
所以int可以存到32bit,但是有一位要留给正负号,所以是31,然后由于还有一个特殊的数0,而0~231-1刚好是231个数。最多表示10位整数。
long long就是-263~263-1。最多表示19位整数。
加法
模拟过程:由于计算是从右向左计算的,进位各种的不太方便。而且字符串每一位只能存入一位字符,所以采用整数数组来存储。为了让计算从左到右进行,打算把两个字符串逆序存入两个整数数组,逐位运算,逐位进位即可。
#include <iostream>
#include <math.h>
using namespace std;
int main() {
string s1, s2; //将两个高精度的数存入字符串中
cin >> s1 >> s2; //如果没有空格的话推荐使用cin输入
//好像说getline在OJ上会误错
int nums1[1000] {0}, nums2[1000] = {0}; //开一个1000的数组将s1与s2逆序存入其中
int len1 = s1.size(), len2 = s2.size(); //提前声明,减少循环次数
for (int i = 0; i < len1; ++i) {
nums1[i] = s1[len1 - 1 - i] - '0'; //由于取出的字符串是字符所以要减去对应的ASCII码
}
for (int i = 0; i < len2; ++i) {
nums2[i] = s2[len2 - 1 - i] - '0';
}
//再开一个数组用于记录相加的位数,还要定义一个整数len为len1与len2的最大值,因为要加len次
int num[1000] = {0}, len = max(len1, len2);
int a = 0; //还要定义一个整数用于记录数组的长度
for (a; a < len; ++a) {
num[a] = nums1[a] + nums2[a]; //先加再进位
}
//进位
for (int i = 0; i < a - 1; ++i) {
if (num[i] >= 10) {
num[i] %= 10;
num[i + 1] += 1;
}
}
//倒序输出
//如果首位进位了的话不能保证输出,所以加了一个判断
if (num[a] != 0) {
cout << num[a];
}
for (int i = 0; i < a; i++) {
cout << num[a - 1 - i];
}
}
减法
减法基本思路跟加法相同,也是逆序存入数组然后每一位对应先减后进位。但是要注意的是,有可能出现结果为负数的情况,所以需要判断一下然后逆过来减。也可以在起初的时候判断s1与s2哪个大,然后交换一下。
#include <iostream>
#include <math.h>
using namespace std;
int main() {
string s1, s2; //将两个高精度的数存入字符串中
cin >> s1 >> s2; //如果没有空格的话推荐使用cin输入
int nums1[1000] = {0}, nums2[1000] = {0}; //开一个1000的数组将s1与s2逆序存入其中
int len1 = s1.size(), len2 = s2.size(); //提前声明,减少循环次数
for (int i = 0; i < len1; ++i) {
nums1[i] = s1[len1 - 1 - i] - '0'; //由于取出的字符串是字符所以要减去对应的ASCII码
}
for (int i = 0; i < len2; ++i) {
nums2[i] = s2[len2 - 1 - i] - '0';
}
int num[1000] = {0}, len = max(len1, len2);
int a = 0;
if (len1 > len2 || (len1 == len2 && s1 > s2)) {
//当s1对应的数大于s2的时候
for (a; a < len; ++a) {
num[a] = nums1[a] - nums2[a];
}
} else if (len1 < len2 || (len1 == len2 && s1 < s2) ) {
//当s1对应的数小于s2的时候
for (a; a < len; ++a) {
num[a] = -nums1[a] + nums2[a];
}
cout << '-';//输出负号
} else if (s1 == s2) {
//会存在一个特殊情况就是s1等于s2的时候返回很多个0,所以单独拎出来
cout << '0';
return 0;//直接结束main主函数
}
for (int i = 0; i < a; ++i) {
if (num[i] < 0) {
num[i] = 10 + num[i];
num[i + 1] = num[i + 1] - 1;
}
}
//找到第一个不为0的数,因为减法可能到最后只剩一位数
for (int j = 0; j < a; ++j) {
if (num[a - 1 - j] != 0) {
a = a - j;
break;
}
}
for (int i = 0; i < a; ++i) {
cout << num[a - 1 - i];
}
}
高精度乘以单精度
还是一样,逐位相乘与此同时进位,需要注意的是,这个算法在最后一个格子中得到的数不需进位直接输出即可。比如986*26,我们会得到6 3 6 25,这个数组,直接逆序输出就可以了,不需要再将25进位为6 5 6 5 2,反而略显麻烦。
#include <iostream>
using namespace std;
int nums1[1000], num[1000];
int main() {
string s1;
int a;
cin >> s1 >> a;
if (a == 0) {
cout << '0';
return 0;
}
int len1 = s1.size();
for (int i = 0; i < len1; ++i) {
nums1[i] = s1[len1 - 1 - i] - '0';
}
int i = 0;
for (i; i < len1; ++i) {
num[i] += nums1[i] * a;
if (num[i] >= 10) {
num[i + 1] += num[i] / 10;
num[i] %= 10;
}
}
if (num[i] != 0) {
cout << num[i];
}
for (int j = 0; j < i; ++j) {
cout << num[len1 - j - 1];
}
}
高精度乘高精度
高精度乘法也是一样的,只不过进位需要注意一下。由于每个位数的乘法都需要进1,所以在双循环的时候采用的是u+v,注意两个len1,len2长的整数相乘,最长是len1+len2,所以要从最后一位往会判断看看首个不为0的是哪个,再从这位开始逆序输出。
int nums1[1000], nums2[1000], num[1000];//全局变量 为了内部元素全为0
int i;
int main() {
string s1, s2;
cin >> s1 >> s2;
if (s1 == "0" || s2 == "0") {
cout << '0';
return 0;
}
int len1 = s1.size(), len2 = s2.size();
for (i = 0; i < len1; ++i) {
nums1[i] = s1[len1 - 1 - i] - '0';
}
for (i = 0; i < len2; ++i) {
nums2[i] = s2[len2 - 1 - i] - '0';//一定要注意ASCII码转码!!
}
int u = 0, v = 0;
for (u = 0; u < len1; ++u) {
for (v = 0; v < len2; ++v) {
int a = nums1[u] * nums2[v];
num[v + u] += a;
//由于乘法需要进位 外层每过去一位都需要进1 所以这里进位与u相关联
if (num[u + v] >= 10) {
num[u + v + 1] += num[u + v] / 10;
num[u + v] %= 10;
}//由于存入了一个新的数组,所以可以边算边进位
}
}
int a = u + v - 1;//逆序查找 数组下标需要减1
for (a; a >= 0; --a) {
if (num[a] != 0) {
i = a;//找到逆序第一个不为0的数
break;
}
}
//从i位开始输出
for (int j = 0; j <= i; ++j) {
cout << num[i - j];
}
}
高精度除法
高精度除法有两种,第一种是有时候对于较小的数要求精确到小数点后100位,第二种是大数运算。
第一种的代码 逐步取余模拟即可
#include <iostream>
using namespace std;
int main() {
int a, b, n, t = 0, i = 0;
cin >> a >> b >> n;
cout << a / b << '.';
t = a % b * 10;
for (i; i < n; i++) {
cout << t / b;
t = (t - t / b * b) * 10;
}
}
第二种代码
这个是大数除以一个精确数的,这个方法要笨一点,要把几种极端点的情况用if列出来
#include <iostream>
#include <string.h>
using namespace std;
int main() {
string s1;
int B, num1[2000] = {0}, num2[2000] = {0}, i = 0;
cin >> s1 >> B;
int len = s1.size();
//cout << len;
//把精确大数逐个输入整数数组
for (int i = 0; i < len; ++i) {
num1[i] = s1[i] - '0';
}
//判断特殊情况
if (num1[i] == 0) {
cout << '0';
} else if (num1[i] < B && len == 1) {
cout << '0';
} else if (num1[i] == B && len == 1) {
cout << "1 0";
return 0;
}
//如果是一位数的话就不会进入这个循环
while (i < len - 1) {
if (num1[i] >= B) {
num2[i] = num1[i] / B;
num1[i] -= num2[i] * B;
} else {
int a = num1[i] * 10 + num1[i + 1];
num2[i + 1] = a / B;
num1[i + 1] = a - num2[i + 1] * B;
i++;
}
}
for (int j = 0; j < len; j++) {
if (num2[0] == 0) {//因为最多只可能第一位为0
num2[0] = 1;
} else {
cout << num2[j];
}
}
cout << " ";
cout << num1[i];
}