超长整数的基础运算 算法实现之加、减篇

上篇介绍了基础的表示结构以及函数介绍,本篇将开始介绍加减实现的过程。

由于整数有正负之分,所以两个整数相加有四种情况: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等将在系列介绍完大整数运算结束后将代码贴出。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章