点击查看代码
//大整数存储
#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;
}