超長整數的基礎運算 算法實現之加、減篇

上篇介紹了基礎的表示結構以及函數介紹,本篇將開始介紹加減實現的過程。

由於整數有正負之分,所以兩個整數相加有四種情況: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等將在系列介紹完大整數運算結束後將代碼貼出。

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