開發環境:Visual C++ 2010 sp1 with FFTW
延續上一次part1的文章,這邊要介紹實際怎麼使用C進行實作,
首先,先定義一個大數的結構(參考:點我)
以下為把數字以字串的型式輸入以及輸出至Console的code:
加法
承如上次所介紹的那樣直接對齊每個位數,進行相加,最後檢查進位就可以了,
這邊再加上了正.負號的判斷,假設要運算c = a + b,運算的情況就會有以下四種:
case 1: a與b都是正的,就兩數直接相加
case 2: a為負,b為正,數學表示式就會是 c = -a + b = b - a
case 3: a為正,b為負,數學表示式就會是 c = a + (-b) = a - b
case 4: a與b都是負的,數學表示式就會是 c = (-a) + (-b) = -( a + b )
以下為C的code:
減法
那麼減法的就跟加法的概念差不多,沒有進位的問題,可是有某個位數不夠減的問題,
要跟比較大的位數借數字來減, 這邊再加上了正.負號的判斷,一樣假設要運算c = a - b,運算的情況就會有以下四種:
case 1: a與b都是正的,就兩數直接相減
case 2: a為負,b為正,數學表示式就會是 c = -a - b = -(a + b)
case 3: a為正,b為負,數學表示式就會是 c = a - (-b) = a + b
case 4: a與b都是負的,數學表示式就會是 c = (-a) - (-b) = - a + b
由於我個人在實作的時候比較喜歡用數字大的-數字小的
所以要先判斷兩個數字絕對值的大小,然後最後再判斷正負的問題,
以下為C的code:
乘法
假設兩個數字要相乘 99 , 12,在直式的乘法裡面:
99
x 12
-----------
198(r1)
+ 99(r2)
-----------
1188
由上面的式子所得知,乘數以最低到最高的位數來乘乘數的每一個位數(上面標的rn),
每乘一round之後並且把往左位移一次相加(例如: r1+r2(左移一個位數)),
計算出來的結果再進行進位的判斷,就可以得到所求,
這邊再加上了正.負號的判斷,
假設要運算c = a * b,運算的情況就會有以下四種:
case 1: a與b都是正的,相乘結果就會是正數
case 2: a為負,b為正,相乘結果就會是負數
case 3: a為正,b為負,相乘結果就會是負數
case 4: a與b都是負的,相乘結果就會是正數
以下為C的code:
在此先暫停一下,因為除法概念上會比較麻煩,下一篇再來講解....
To be continued...
延續上一次part1的文章,這邊要介紹實際怎麼使用C進行實作,
首先,先定義一個大數的結構(參考:點我)
//可以儲存100個位數
#define BIG_SIZE 100
//數字以10進位的方式處理
#define BIG_CAP 10
typedef struct tagBig{
int arr[BIG_SIZE]; //大數位數的陣列
size_t dig; // 這個數字的總位數
bool flag ; //是否為負數( true -> 負 )
tagBig() { //初始化這個變數
//陣列內容初始化為0
memset( arr , 0 , sizeof(size_t) * BIG_SIZE ) ;
//這個數字為正的
flag = false ;
} ;
}Big;
以下為把數字以字串的型式輸入以及輸出至Console的code:
// 輸入
void big_from_string(Big * big , const char * str)
{
int i, j, len, pwr;
int * arr = big->arr;
// 去前導0
while(*str=='0' && *str!='\0') ++str;
// 轉入大數
len = strlen(str);
arr[0] = 0;
for(j=0, i=len-1; i>=0; ++j, arr[j]=0)
for(pwr=1; pwr!=BIG_CAP && i>=0; pwr*=10, --i)
arr[j]+=(str[i]-'0')*pwr;
// 計算位數
big->dig = j;
}
// 輸出
void big_print(Big * big)
{
int i = big->dig;
if ( big->flag )
printf("-") ;
if(!i) printf("0");
else {
int *arr = big->arr;
printf("%d", arr[--i]); // 輸出第一位數字
for(--i; i>=0; --i) // 輸出其他數字
printf("%0*d", BIG_LEN, arr[i]);
}
}
加法
承如上次所介紹的那樣直接對齊每個位數,進行相加,最後檢查進位就可以了,
這邊再加上了正.負號的判斷,假設要運算c = a + b,運算的情況就會有以下四種:
case 1: a與b都是正的,就兩數直接相加
case 2: a為負,b為正,數學表示式就會是 c = -a + b = b - a
case 3: a為正,b為負,數學表示式就會是 c = a + (-b) = a - b
case 4: a與b都是負的,數學表示式就會是 c = (-a) + (-b) = -( a + b )
以下為C的code:
//c = a + b
int big_add(const Big *a ,const Big *b, Big *c) {
memset( (void*)c , 0 , sizeof(Big) ) ;
// (a & !b) | (a & b) = a xor b
//if ( a->flag && !b->flag || !a->flag && b->flag ) { //如果其中一個為負的
if ( a->flag ^ b->flag ) {
if ( a->flag ) { //如果a為負的
//c = -a + b = b - a
return big_sub( b , a , c ) ;
} else { //如果b是負的
//c = a + (-b) = a - b
return big_sub( a , b , c ) ;
}
} else { //如果兩個都是正的或者是兩個都是負的
size_t maxDig = ( a->dig > b->dig ) ? a->dig : b->dig ;
size_t i = 0 , tmp ;
int over = 0 ;
for ( i = 0 ; i < maxDig ; i++ ) {
//tmp = a + b + c(有進位的問題,所以要先加原本的數值)
tmp = a->arr[i] + b->arr[i] + c->arr[i];
//把數值算回去
c->arr[i] = ( tmp >= BIG_CAP) ? tmp % BIG_CAP : tmp ;
if ( i + 1 > BIG_SIZE ) {
over = -1 ;
break ;
}
//處理進位
c->arr[i+1] = tmp / BIG_CAP ;
}
//最後計算總位數,判斷有沒有進位到maxDig的下一位數
c->dig = maxDig ;
while ( c->arr[c->dig] && c->dig < BIG_SIZE)
c->dig++ ;
//c->dig = (c->arr[i + 1]) ? maxDig + 1 : maxDig ;
c->flag = ( a->flag ) ;
return over ;
}
}
減法
那麼減法的就跟加法的概念差不多,沒有進位的問題,可是有某個位數不夠減的問題,
要跟比較大的位數借數字來減, 這邊再加上了正.負號的判斷,一樣假設要運算c = a - b,運算的情況就會有以下四種:
case 1: a與b都是正的,就兩數直接相減
case 2: a為負,b為正,數學表示式就會是 c = -a - b = -(a + b)
case 3: a為正,b為負,數學表示式就會是 c = a - (-b) = a + b
case 4: a與b都是負的,數學表示式就會是 c = (-a) - (-b) = - a + b
由於我個人在實作的時候比較喜歡用數字大的-數字小的
所以要先判斷兩個數字絕對值的大小,然後最後再判斷正負的問題,
以下為C的code:
//交換兩個數字
void swap( Big* a , Big* b) {
Big tmp = *a ;
*a = *b ;
*b = tmp ;
}
//比較兩個數字的大小
int big_compare(const Big *a ,const Big *b ) {
int maxDig = ( a->dig > b->dig ) ? a->dig : b->dig ;
// (a & !b) | (a & b) = a xor b
//if ( !a->flag && !b->flag || a->flag && b->flag) { //如果兩個數字都是正的或者是負的
if ( !(a->flag ^ b->flag) ) {
bool bothPlus = !a->flag ;
if ( a->dig > b->dig ){
if ( bothPlus ) return 1 ;
else return -1 ;
}else if ( a->dig < b->dig ) {
if ( bothPlus ) return -1 ;
else return 1 ;
}
while ( maxDig-- ) {
if ( a->arr[maxDig] > b->arr[maxDig] ) {
if ( bothPlus ) return 1 ;
else return -1 ;
}
else if ( a->arr[maxDig] < b->arr[maxDig] ) {
if ( bothPlus ) return -1 ;
else return 1 ;
}
}
return 0 ;
} else {
if ( a->flag ) //如果a是負的
return -1 ;
else //如果b是負的
return 1 ;
}
/* else if ( a->flag && b->flag ) { //如果兩個數字都是負的
if ( a->dig > b->dig ) return -1 ; //如果a的位數多於b的位數,表示b比較大
else if ( a->dig < b->dig ) return 1 ; //如果a的位數少於b的位數,表示a比較大
while ( maxDig-- ) {
if ( a->arr[maxDig] > b->arr[maxDig] ) return -1 ;
else if ( a->arr[maxDig] < b->arr[maxDig] ) return 1 ;
}
return 0 ;
}*/
}
//c = a - b
int big_sub(const Big *a ,const Big *b, Big *c) {
memset( (void*)c , 0 , sizeof(Big) ) ;
Big *_a = (Big*)malloc( sizeof( Big ) ) ;
Big *_b = (Big*)malloc( sizeof( Big ) ) ;
int over = 0;
//如果其中一個為負的
//if ( a->flag && !b->flag || !a->flag && b->flag ) {
// (a & !b) | (a & b) = a xor b
if ( a->flag ^ b->flag ) {
bool flag = false ;
if ( !a->flag ) { // c = a - (-b) = a + b ;
memcpy( (void*) _a , (void*) b , sizeof( Big ) ) ;
memcpy( (void*) _b , (void*) a , sizeof( Big ) ) ;
}
else { // c = -a - b = - ( a + b )
memcpy( (void*) _a , (void*) a , sizeof( Big ) ) ;
memcpy( (void*) _b , (void*) b , sizeof( Big ) ) ;
flag = true ;
}
_a->flag = false ;
//在減法的情況下,數值會溢位只會在兩個為同樣符號的情況下,也就是在兩數相加的情況下才會發生
over = big_add( _a , _b , c ) ;
c->flag = flag ;
} else { //如果兩數都為正/負的 ,c = a - b or c = -a - (-b)
int comp ;
if ( a->flag ) { //c = -a - (-b) = -a + b = b - a
memcpy( (void*)_b , (void*)a , sizeof(Big) ) ; //b = -a
memcpy( (void*)_a , (void*)b , sizeof(Big) ) ; //a = b
_a->flag = _b->flag = false ;
comp = big_compare( _a , _b ) ;
_a->flag = false ;
if ( comp < 0 ) // if (abs(a) < abs(b))
swap( _a , _b ) ;
} else { //c = a - b
memcpy( (void*)_a , (void*)a , sizeof(Big) ) ;
memcpy( (void*)_b , (void*)b , sizeof(Big) ) ;
_a->flag = _b->flag = false ;
comp = big_compare( _a , _b ) ;
if ( comp < 0 ) // if (abs(a) < abs(b))
swap( _a , _b ) ;
//_a->flag = _b->flag = true ;
}
/*
the number of a is must greator than b
c = a - b ;
*/
size_t maxDig = ( _a->dig > _b->dig ) ? _a->dig : _b->dig ;
long i , tmp ;
for ( i = 0 ; i < (long)maxDig ; i++ ) {
tmp = _a->arr[i] - _b->arr[i] + c->arr[i] ;
if ( tmp >= 0 )
c->arr[i] = tmp ;
else {
c->arr[i] = BIG_CAP + tmp ;
c->arr[i+1]-- ; //next carry -= 1
}
}
//if ( comp < 0 )
// c->flag = true ;
//else
// c->flag = false ;
c->flag = ( comp < 0 ) ;
c->dig = maxDig ;
while ( c->arr[c->dig-1] == 0 && c->dig > 0)
c->dig -- ;
}
free( (void*)_a ) ;
free( (void*)_b ) ;
return over ;
}
乘法
假設兩個數字要相乘 99 , 12,在直式的乘法裡面:
99
x 12
-----------
198(r1)
+ 99(r2)
-----------
1188
由上面的式子所得知,乘數以最低到最高的位數來乘乘數的每一個位數(上面標的rn),
每乘一round之後並且把往左位移一次相加(例如: r1+r2(左移一個位數)),
計算出來的結果再進行進位的判斷,就可以得到所求,
這邊再加上了正.負號的判斷,
假設要運算c = a * b,運算的情況就會有以下四種:
case 1: a與b都是正的,相乘結果就會是正數
case 2: a為負,b為正,相乘結果就會是負數
case 3: a為正,b為負,相乘結果就會是負數
case 4: a與b都是負的,相乘結果就會是正數
以下為C的code:
//c = a * b
int big_mul(const Big *a ,const Big *b, Big *c) {
memset( (void*)c , 0 , sizeof(Big) ) ;
bool flag = a->flag ^ b->flag ;
//sign check
//if ( !a->flag && b->flag || a->flag && !b->flag )
// flag = true ;
size_t i , j , _x , tmp ;
// Time complex : O( length(a) * length(b) )
for ( i = 0 ; i < a->dig ; i++ ) {
if ( !a->arr[i] ) continue ;
_x = a->arr[i] ;
for ( j = 0 ; j < b->dig ; j++ ) {
tmp = c->arr[i+j] + _x * b->arr[j] ;
c->arr[i+j] = tmp % BIG_CAP ;
//判斷overflow
if ( i+j+1 > BIG_SIZE ) return -1 ;
c->arr[i+j+1] += tmp / BIG_CAP ;
}
}
c->flag = flag ;
c->dig = a->dig * b->dig ;
while ( c->arr[c->dig] )
c->dig-- ;
return 0 ;
}
在此先暫停一下,因為除法概念上會比較麻煩,下一篇再來講解....
To be continued...
沒有留言:
張貼留言