前言
这是51nod进阶题库中编号为1005的一道题目,题目非常简单,但是需要考虑的细节问题有很多,我们接下来分析一下该题目如何实现。
一、题目概述
给出2个大整数A,B,计算A+B的结果。
二、实现思路
题目非常简单,读完题目以后首先我们需要知道什么是大整数?
大整数
想知道什么是大整数,不如换一个解释的方法:什么时候需要用到大整数?众所周知,int类型的范围是-2147483648~2147483647。long long类型的范围是 -2^63 ~ (2^63)-1。那当题目需要用到或需要输出比long long类型的范围还要大的数字时,我们是不是就不能用常规办法去接收这些数字了。这个时候使用大整数就可以解决上述问题。即解决接收超出long long范围数字的问题。
既然大整数无法用常规的类型来进行保存,我们就要用到另一种方式来保存大整数,对于大整数的保存一般采用字符数组的形式来进行保存。
保存之后我们进行运算,由于加法运算是从低位到高位进行相加,而我们保存的字符大整数是从高位到低位进行保存的,还存在字符首位是 “-” 的情况,所以我们需要将字符数组转化为数字数组并逆序保存,方便后面运算。
分类
大整数的加法可以分成三类:
- 正正相加
两个正的大整数相加和加法运算没有区别,需要考虑的只有进位问题和相加后结果的位数。 - 正负相加
一正一负的情况我们需要考虑结果的正负问题,首先我们将负大整数去掉负号以后和正大整数比较字符长度,结果的正负取长度长的数据。
如果两个长度相等,此时我们就要逐位比较大小来确定结果正负。
确定完正负以后,按位相减,注意借位。 - 负负相加
同负的情况和同正的计算方法一样,只需将结果取负即可。
三、实现过程
1.定义大整数结构体
#define MAX 100
struct long_num
{
char s_num[MAX]; // 字符数组
int num[MAX]; // 数字数组
size_t size; // 字符长度
bool SIGNEN_FLAG;// 正负标志 bool为正 false为负
};
2.实现相加
struct long_num add(struct long_num *f,struct long_num *s) {
int i = 0;
struct long_num temp={{0},{0},0,true};
// 计算长度
f->size = strlen(f->s_num);
s->size = strlen(s->s_num);
// 字符数组 转化为 数字数组 并 逆置 判断正负
if (f->s_num[0] == '-')// 判断正负 负
{
f->SIGNEN_FLAG = false;
for (i = 1; i < f->size; i++)// f
{
f->num[f->size - i - 1] = f->s_num[i] - '0';
}
}
else// 正
{
f->SIGNEN_FLAG = true;
for (i = 0; i < f->size; i++)// f
{
f->num[f->size - i - 1] = f->s_num[i] - '0';
}
}
//同上
if (s->s_num[0] == '-')
{
s->SIGNEN_FLAG = false;
for (i = 1; i < s->size; i++)// s
{
s->num[s->size - i - 1] = s->s_num[i] - '0';
}
}
else
{
s->SIGNEN_FLAG = true;
for (i = 0; i < s->size; i++)// s
{
s->num[s->size - i - 1] = s->s_num[i] - '0';
}
}
// 实现相加
if(f->SIGNEN_FLAG && s->SIGNEN_FLAG)// 同正
{
for ( i = 0; i < max(f->size,s->size); ++i)
{
temp.num[i] += f->num[i] + s->num[i]; // 对应位相加
if (temp.num[i] > 9) // 判断是否进位
{
temp.num[i] -= 10;
temp.num[i + 1] += 1; // 高位加一
}
}
temp.SIGNEN_FLAG = true;// 确定结果正负
if(temp.num[i] != 0)//确定结果有多少位
temp.size = max(f->size,s->size) + 1;
else
temp.size = max(f->size,s->size);
}
// 同负 原理和同正一样 结果为负
if(f->SIGNEN_FLAG == false && s->SIGNEN_FLAG == false)
{
for ( i = 0; i < max(f->size,s->size) - 1 ; ++i)
{
temp.num[i] += f->num[i] + s->num[i];
if (temp.num[i] > 9)
{
temp.num[i] -= 10;
temp.num[i + 1] += 1; // 高位加一
}
}
temp.SIGNEN_FLAG = false;
if(temp.num[i] != 0)
temp.size = max(f->size,s->size) + 1;
else
temp.size = max(f->size,s->size);
}
// 一正一负
if(f->SIGNEN_FLAG == false && s->SIGNEN_FLAG == true)
temp = count(f,s);
if(s->SIGNEN_FLAG == false && f->SIGNEN_FLAG == true)
temp = count(s,f);
//temp.size = max(f->size,s->size);
if(temp.SIGNEN_FLAG == true) // 结果为正 将数字数组值逆序存入字符数组
{
for ( i = 0; i < temp.size; ++i)
temp.s_num[temp.size - 1 - i] = temp.num[i] + '0';
}
else// 结果为负 将负号存入字符数组首位 将数字数组值逆序存入字符数组
{
temp.s_num[0] = '-';
for ( i = 0; i < temp.size - 1; ++i)
temp.s_num[temp.size - 1 - i] = temp.num[i] + '0';
}
return temp;
}
3正负相加的实现
struct long_num count(struct long_num *f,struct long_num *s)// 计算一正一负的类型 第一个为负 第二个为正
{
int i;
struct long_num temp = {{0},{0},0,true}; /// 中间值 保存结果
if(f->SIGNEN_FLAG == false && s->SIGNEN_FLAG == true) // 第一参数为负 第二参数为正
{
if (f->size -1 > s->size)// 剔除负号 比较数据长度 如果 f 长度 大于 s 长度
{
for ( i = 0; i < (f->size - 1); ++i)
{
temp.num[i] += f->num[i] - s->num[i]; //做减法运算 存入temp
if (temp.num[i] < 0) // 判断是否借位
{
temp.num[i] += 10;
temp.num[i + 1] -= 1;
}
}
temp.SIGNEN_FLAG = false; // 结果为负
temp.size = f->size;
for (int j = i-1; j > 0; --j)//去前置0
{
if(temp.num[j] != 0)
break;
else
temp.size--;
}
}
if (f->size - 1 < s->size)// 如果 f 长度 小于 s 长度 结果为正
{
for (i = 0; i < (s->size); ++i)
{
temp.num[i] += s->num[i] - f->num[i];
if (temp.num[i] < 0)
{
temp.num[i] += 10;
temp.num[i + 1] -= 1;
}
}
temp.SIGNEN_FLAG = true;
temp.size = s->size;
for (int j = i-1; j > 0; --j)//去前置0
{
if(temp.num[j] != 0)
break;
else
temp.size--;
}
}
if (f->size -1 == s->size)// 如果 f 长度 等于 s 长度
{
//char * str = f->s_num+1;
int compare = strcmp(f->s_num + 1,s->s_num);// 判断谁的值大
if(compare == 0) // 两值相等
{
temp.s_num[0] = '0';
temp.size = 1;
}
if(compare < 0) // f 的值小于 s 的值
{
for (i = 0; i < (f->size - 1); ++i)
{
temp.num[i] += s->num[i] - f->num[i];
if (temp.num[i] < 0) // 是否借位
{
temp.num[i] += 10;
temp.num[i + 1] -= 1;
}
}
temp.SIGNEN_FLAG = true;
temp.size = s->size;
for (int j = i-1; j > 0; --j)// 剔除前置0
{
if(temp.num[j] != 0)
break;
else
temp.size--;
}
}
if(compare > 0) // f 的值小于 s 的值
{
for (i = 0; i < (f->size - 1); ++i)
{
temp.num[i] += f->num[i] - s->num[i];
if (temp.num[i] < 0)
{
temp.num[i] += 10;
temp.num[i + 1] -= 1;
}
}
temp.SIGNEN_FLAG = false;
temp.size = f->size;
for (int j = i - 1; j > 0; --j)
{
if (temp.num[j] != 0)
break;
else
temp.size--;
}
}
}
}
return temp;// 返回结果
}
4结果测试
遇到的问题
在进行正负大整数相加运算时,当两个数字数组长度相同时,例如-1732和1700进行运算时,输出结果为-0032,前面的0也保存了下来,这是不需要的,这是由于结果的字符长度计算出现了问题 我们需要剔除前置0,
解决代码如下:
for (int j = i-1; j > 0; --j)//去前置0
{
if(temp.num[j] != 0)
break;
else
temp.size--;
}
到这里大整数的加法基本实现,以上是个人的一些拙见,请各位大佬指正!