大整数存储和四则运算

点击查看代码
//大整数存储
#include<cstdio>
#include<cstring> //使用strlen()和memset()
#pragma warning(disable:4996)

//bign结构体存储大整数的值和它的位数
struct bign {
	int d[1000]; //对大整数进行操作一般都从低位到高位枚举,因此将大整数的低位存储在d[]的低位
	int len;
	bign() { //构造函数初始化
		memset(d, 0, sizeof(d)); //d[]用0初始化
		len = 0; //len初始为0
	}
};

//使用字符串格式读入的大整数存入str[]中,此时大整数的高位对应str[]的低位,因此要将str[]逆序赋值给bign变量
bign change(char str[]) {
	bign a;
	a.len = strlen(str);
	for (int i = 0; i < a.len; i++) {
		a.d[i] = str[a.len - 1 - i] - '0'; //逆序赋值,减去ASCII码的0,将char转换为int
	}
	return a;
}

//比较两个bign变量a和b,a比b大、a和b相等、a比b小分别返回1、0、-1
int compare(bign a, bign b) {
	if (a.len > b.len) return 1; //a比b长,a大
	else if (a.len < b.len) return -1; //a比b短,b大
	else { //a和b长度相等,从高位到低位枚举,比较每一位
		for (int i = a.len - 1; i >= 0; i--) {
			if (a.d[i] > b.d[i]) return 1; //a大于b,a大
			else if (a.d[i] < b.d[i]) return -1; //a小于b,b大
		}
		return 0; //a和b相等
	}
}

/*大整数高精度加法a+b,高精度加法要求a和b都是非负整数
如果其中一个是负整数,则在转换到数组时去掉其负号,然后进行高精度减法
如果两者都是负整数,则在转换到数组时都去掉负号,然后进行高精度加法,计算完再加上负号   
*/
bign add(bign a, bign b) {
	bign c; //存储a+b的结果
	int carry = 0; //进位
	for (int i = 0; i < a.len || b.len; i++) { //以a和b中较长者为界限(较短者扫描结束后,它的其余位都是0不影响计算)
		int temp = a.d[i] + b.d[i] + carry;	//a和b当前位与进位相加
		c.d[c.len++] = temp % 10; //将相加后的个位数作为当前位结果
		carry = temp / 10; //将相加后的十位数作为下一轮计算的进位
	}
	if (carry != 0) { //最高位计算结束后如果进位不为0,则将进位直接存入c中最高位即可
		c.d[c.len++] = carry;
	}
	return c; //返回结果c
}

//大整数高精度减法a-b(要保证a大于b,如果a小于b则要先交换两者,然后输出负号,再调用sub())
bign sub(bign a, bign b) {
	bign c;
	for (int i = 0; i < a.len || i < b.len; i++) {
		if (a.d[i] < b.d[i]) { //当前位a<b不够减
			a.d[i + 1]--; //a向高一位借一
			a.d[i] += 10; //a的当前位加10
		}
		c.d[c.len++] = a.d[i] - b.d[i];	//当前位a-b,结果存入c中
	}
	//如果a的最高位被借位后刚好变为0,且b的最高位也是0,此时相减结果是0,0被存入c的最高位中,因此需要删掉c中这个0
	//如果c中从最高位开始有连续多个0,也需要把这些无意义的0全部删除。如果a-b结果全是0,则只需要保留个位那个0,即c.d[0]
	while (c.len - 1 >= 1 && c.d[c.len - 1] == 0) {
		c.len--; //从c中最高位到低位开始遍历,删除无意义的0,注意不需要处理c.d[0]
	}
}

//高精度与低精度的乘法a*b(如果a和b中存在负数,需要先记录负号,然后将它们的绝对值传给multi())
bign multi(bign a, int b) {
	bign c;
	int carry = 0;
	for (int i = 0; i < a.len; i++) {
		int temp = a.d[i] * b + carry; //高精度当前位与低精度整体相乘,然后加上进位
		c.d[c.len++] = temp % 10; //乘积的个位作为当前位的结果
		carry = temp / 10; //乘积的高位部分作为新的进位
	}
	while (carry != 0) { //计算结束后进位不为0,进位还可能不止一位
		c.d[c.len++] = carry % 10; //对进位的个位进行循环取余,存入c中
		carry /= 10; //进位除以10作为下一轮计算的基准
	}
}

//高精度与低精度的乘法a/b,有的题目要求输出余数,因此传送一个引用型r变量保存余数
bign divide(bign a, int b,int& r) {
	bign c;
	c.len = a.len; //被除数的每一位和商的每一位是一一对应的,开始先令c等于a的长度
	for (int i = a.len - 1; i >= 0; i--) { //除法从高位到低位遍历
		r = r * 10 + a.d[i]; //将上一轮余数扩大10倍然后加上当前位被除数a,作为本轮的被除数
		if (r < b) c.d[i] = 0; //r小于b不够除,这一位的商为0
		else { //r大于等于b,够除
			c.d[i] = r / b;	//存储这一位的商
			r = r % b; //更新余数
		}
	}
	//a除以b后,商的最高位可能存在连续的无意义0,因此要把这些0删掉(第一位c.d[0]不需要处理)
	while (c.len - 1 >= 1 && c.d[c.len - 1] == 0) {
		c.len--;
	}
	return c;
}

posted @ 2022-09-29 21:04  zhaoo_o  阅读(18)  评论(0编辑  收藏  举报