上篇介绍了基础的表示结构以及函数介绍,本篇将开始介绍加减实现的过程。
由于整数有正负之分,所以两个整数相加有四种情况:a+b,a+(-b),(-a)+b和-a+(-b)。
对于a+b和-a+(-b),可以通过对数组对应位元素相加即可,主要是考虑进位问题。而a+(-b)和(-a)+b,
其实就是两个大整数的减法,其主要考虑的问题是向高位借位的问题。所以减法可以通过加法的运算得到结果。
1、符号相同的加法运算
符号相同的加法运算的实现过程是基于这样的一个原因:因为两个整数的符号相同,可以直接对两个整数数组对应位元素相加,并考虑进位问题。因为两个整数的数位存在以下三种情况(不妨设两个大整数分别为src1和src2):src1->length == src2->length;src1->length > src2->length; src1->length < src2->length。所以在处理加法的时候,对数位较大的整数一分为二,第一部分是跟另一大整数的长度相等。可以先把相同长度部分先计算,然后再对多出的长度部分直接赋值,这样可以不用考虑谁大谁小问题,而且可以加快运算速度。如下表所示:
分段运算过程:
1 2 3 |
4 5 6 7 8 9 |
— — — |
4 5 6 7 8 9 |
— — — |
9 1 3 5 7 8 |
1 2 3 |
9 1 3 5 7 8 |
2、符号不同的加法运算,即减法运算
符号不同的加法运算的过程,也是跟符号相同的加法过程类似。也是对数位较大的整数进行分段,不过,它也有不同的地方。因为符号不相同,这个加法就是减法。因为不知道是哪个大整数较大,当它们做减法运算的时候,就有可能会在最高数位出现负数问题,而大整数各位元素都为正数,所以要多处理一步,即对负数转换成正整。而如果把无符号整数的较大者作为被减数,就可以省略这一步。所以此运算过程是:先对两个大整数进行无符号比较,最大者作为被减数,然后进行减法操作。
/*
加法运算
*/
int addHBInt(HBigInt* dst, HBigInt* src1, HBigInt* src2){
long dstlen=0, len=0, i;
un_short mark = 0; //进位标志
un_short result; //数组对应元素相加结果
HBigInt *pSrc=NULL;
len = (src1->length >= src2->length) ? src1->length : src2->length;
dstlen = len;
if(FAILE_MEMORY_BINT == extendHBInt(dst,len)) return FAILE_MEMORY_BINT;
//较小数位的整数的长度
len = (src1->length>src2->length) ? src2->length : src1->length;
//对两个大整数的数位相同部分进行计算
for(i = 0; i < len; i++) {
result = src1->pBigInt[i] + src2->pBigInt[i] + mark;
dst->pBigInt[i] = (result & 0xffff);
mark = result >> 16;
}
//对较大的大整数数位多出部分进行计算
pSrc = (src1->length > src2->length) ? src1 : src2;
while(i < pSrc->length) {
result = pSrc->pBigInt[i] + mark;
dst->pBigInt[i] = result & 0xffff;
mark = result >> 16;
i++;
}
if(mark) {
dst->pBigInt[i] = mark;
dstlen = pSrc->length + 1;
}
dst->sign = src1->sign;
dst->length = dstlen;
return RETURN_OK_BINT;
}
/*
减法运算(保证是同号相减,否则采用加法实现)
*/
int subHBInt(HBigInt* dst, HBigInt* src1, HBigInt* src2) {
long i=0,len_max = 0,len_min=0 ;
un_short *psrc_max,*psrc_min,*pdst;
long dstlen = dst->alloclen; //目标数组分配的最大值
un_short mark = 0; //借位标志
<span style="white-space:pre"> </span>long result = 0; //数组对应元素相加结果
int sign = src1->sign; //整数的符号
int re;
len_max = (src1->length >= src2->length) ? src1->length : src2->length;
len_min = (src1->length > src2->length) ? src2->length : src1->length;
psrc_max=src1->pBigInt;
psrc_min=src2->pBigInt;
pdst = NULL;
if(FAILE_MEMORY_BINT == extendHBInt(dst,len_max)) return FAILE_MEMORY_BINT;
re = compareHBInt(src1,src2); //对两个大整数进行无符号比较
// 两个大整数大小相等,进行加法运算后位0
if(re == 0) {
dst -> length = 0;
dst -> sign = 1;
return RETURN_OK_BINT;
}
// src2比src1大,交换临时指针变量,保证psrc1始终大于psrc2
if(re == -1) {
sign = -1;
psrc_max = src2->pBigInt;
psrc_min = src1->pBigInt;
len_min = src1->length;
}
// 对两整数相同长度部分进行计算
for(i=0; i<len_min; i++) {
if((result = (psrc_max[i]-psrc_min[i]-mark))<0) mark = 1; // 向高一位借1
else mark = 0;
if(mark) result += CARRY_RADIX; //高位借1后,该值变成正值
dst->pBigInt[i] = result;
}
// 对数位较大部分的借位计算
while(i < len_max) {
result = psrc_max[i] - mark;// 给低位借走1
if(result >= 0) {
dst->pBigInt[i++] = result;
//mark = 0;
break; // 剩余高位直接赋值,不需要再循环处理
} else { //表示向高一位借了1
dst->pBigInt[i++] = result + CARRY_RADIX;
}
}
// 剩余高位直接赋值
memcpy(dst->pBigInt+i,psrc_max+i,sizeof(un_short)*(len_max-i));
dst->length = len_max;
dst->sign = sign;
trimHBInt(dst); //对结果去掉前面的0,当最高位为0时,需要修改length的值
return 1;
}
说明:以上函数中使用到的其他函数,例如extendHBInt和assignHBInt等将在系列介绍完大整数运算结束后将代码贴出。