c·c++/c 프로그래밍

큰수의 나눗셈 - 0x100000000 진법

바로이순간 2013. 8. 2. 19:39

#include <stdio.h>

#include <string.h>

#define MAX 1000

#define MAX2 2000 // 10진수 출력을 위한 수, 10진수 16000 자리 까지 허용

#define MAX8 8000 // 입력되는 수는 8000 자리 까지 허용


//  0번째 칸에는 자리수를 저장합니다. 16진수 0x100000000진법을 사용합니다.

// 1번째 칸에는 일의 자리, 2번째 칸에는 16진수억의 자리, 3번째 칸에는 16진수 조의 자리

// ... 등을 저장합니다. 최대 7996자리까지 출력이 가능합니다.


// notation 1: hex, 2: oct, 3: bin, 4: dec

void printResult(long long [], int);

void copy(long long a[], long long b[]) {

    int i, n=a[0];

    for(i=0;i<MAX;i+=1) { b[i]=a[i]; }

}

void clear(long long a[]) {

    int i;

    for(i=0;i<MAX;i+=1) { a[i]=0; }

    a[0]=1;

}

int compare(long long a[], long long b[]) {

    int i=a[0];

    if(a[0]<b[0]) { return -1; }

    if(a[0]>b[0]) { return 1; }

    while(i>0) {

        if(a[i]<b[i]) { return -1;  }

        if(a[i]>b[i]) { return 1; }

        i-=1;

    }

    return 0;

}

void add(long long a[], long long b[], long long r[]) {

    int i, j;

    long long x=0;

    clear(r);

    r[0]=a[0];

    if(b[0]>r[0]) { r[0]=b[0]; }

    for(i=1;i<=r[0];i+=1) {       // 수의 각 자리와 밑에서 올라온 수를

        x=a[i]+b[i]+x;             // 더해줍니다.

        r[i]=x%0x100000000LL;             // 값이 9999보다 크면 제자리의 수만 남고

        x=x/0x100000000LL;                // 윗자리로 올라갑니다.

    }

    if(x!=0) { 

        r[0]+=1;

        r[r[0]]=x;

    } 

}

void sub(long long a[], long long b[], long long r[]) {

    int i, j;

    long long x=0;


    clear(r);

    r[0]=a[0];

    if(b[0]>r[0]) { r[0]=b[0]; }


    for(i=1;i<=r[0];i+=1) { // 수의 각 자리로

        x=a[i]-b[i]+x;      // 결과에 더해줍니다.

        if(x<0) {

            r[i]=x+0x100000000LL;

            x=-1;

        } else {

            r[i]=x;

            x=0;

        }

    }


    if(x!=0) { 

        for(i=0;i<a[0]+1;i+=1) { printf("%08x ", a[i]); } printf("\n");

        for(i=0;i<b[0]+1;i+=1) { printf("%08x ", b[i]); } printf("\n");


        printf("*****************ERROR*************\n");

        getchar(); getchar();

    } 

    i=r[0];

    while(r[i]==0) { i-=1; }

    if(i==0) { i=1; }

    r[0]=i;

    //for(i=0;i<10;i+=1) { printf("%d ", r[i]); } printf("\n");

}

void mul(long long a[], long long b[], long long r[]) {

    int i, j, k;

    unsigned long long x, y;

    clear(r);

    //printf("a = ");

    //printResult(a, 1);

    //printf("b = ");

    //printResult(b, 1);

    for(i=1;i<=b[0];i+=1) {         // 승수의 각 자리로

        for(j=1;j<=a[0];j+=1) {     // 피승수의 각자리를 곱합니다.

            x=b[i]*a[j];

            y=r[i+j-1];

            y=y+x;

            r[i+j-1]=y%0x100000000LL;

            x=y/0x100000000LL;

            r[i+j]+=x;

        }

    }

    r[0]=a[0]+b[0];   // 결과의 자리수를 구합니다.

    if(r[r[0]]==0) { r[0]-=1; }

}

void div(long long a[], long long b[], long long q[], long long r[]) {

    long long v1[MAX]={1,};

    long long v2[MAX]={1,};

    long long one[MAX]={1,1,};

    long long two[MAX]={1,};

    int i, j, e1, e2, ex;

    long long x;

    double d1, d2, dd;


    clear(q);

    copy(a, r); // 피제수를 나머지에 복사한다. 

                // 당분간 나머지가 임시로 남아있는 피제수 노릇을 한다.


    //printf("[r] = ");

    //printResult(r, 1);


    d2=0;       // 제수의 근사값을 d2와 e2로 나타낸다.

    i=b[0];

    j=0;

    while(i>0 && j<2) {

        d2=0x100000000LL*d2+b[i];

        i-=1; 

        j+=1;

    }

    e2=i;

 

    // 남아있는 피제수가 제수보다 클 동안 계속한다.

    while(compare(r, b)>=0) { 

        // 피제수의 근사값을 d1과 e1으로 나타낸다. 

        d1=0;                 

        i=r[0];

        j=0;

        while(i>0 && j<2) {

            d1=0x100000000LL*d1+r[i];

            i-=1; 

            j+=1;

        }    

        e1=i;


        // 임시 몫의 근사값을 dd와 ex로 나타낸다

        dd=d1/d2;            


        while(dd>0x100000000LL) { dd/=0x100000000LL; e1+=1; }

        //printf("dd=%g e1=%d e2=%d\n", dd, e1, e2); getchar();


        ex=e1-e2+1; 


        clear(v1);

        // 임시 몫은 v1이다. dd와 ex로 부터 v1을 구한다.

        v1[0]=ex;            

        i=0;

        while(ex>0&&i<2) {

            x=dd; dd-=x; dd*=0x100000000; v1[ex]=x; ex-=1; i+=1;

        }

        


        // v1이 클 경우 빼주는 값을 one이라한다.

        clear(one);          

        one[ex+1]=1;

        one[0]=ex+1;


        // v1의 아래 자리는 모두 0으로 채운다. 

        if(i==0) {           

            while(ex>0) { 

                v1[ex]=0; ex-=1; 

            } 

        }


        // 임시몫 v1이 너무 크다면 줄여준다.

        while(1) {

            // 임시 몫 v1과 제수를 곱한 결과를 v2라 한다.

            mul(v1, b, v2);

            // 임시 피제수가 v2보다 크다면 정상적으로 진행한다.

            if(compare(r,v2)>=0) { break; }

            // 만약 임시 피제수보다 v2가 크다면 위에서 구한 임시 몫 v1이 과도하게 크다.

            // v1에서 one을 빼준다.

            sub(v1, one, two);

            copy(two, v1);

        } 

        // 임시 피제수에서 v2를 뺀 결과를 임시 피제수로 한다.


        sub(r, v2, two);

        copy(two, r);

        // q에 v1을 더한 결과를 q로 한다.

        add(q, v1, two);

        copy(two, q);

    }   

    // q의 가장 큰 자리가 0일 경우는 1 자리를 줄여준다.

    while(q[q[0]]==0) { q[0]-=1; }

    if(q[0]<1) { q[0]=1; }

    // r에는 나머지가 남아 있게 된다.

}

// 16진수를 10진수로 바꾸어준다.

void convertResult(long long result[], int d[]) {

    long long a[MAX]={1,};  // 피제수

    long long b[MAX]={1,};  // 제수

    long long q[MAX]={1,};  // 몫

    long long r[MAX]={1,};   // 나머지


    int i=1, x;


    // 십진수 억진법을 사용해서 나머지와 몫을 계속해서 구해갑니다.

    b[1]=100000000; 

    copy(result, a);

    while(1) {

        div(a, b, q, r);

        d[i]=r[1];

        i+=1;

        copy(q,a);

        if(a[0]==1&&a[1]==0) { break; }

    }

    d[0]=i-1;

}

// 출력은 다시 큰 수 부터 출력해 준다.

void printResult(long long result[], int notation) {

    int    decimal[MAX2]={1,};


    const int hex=1, oct=2, bin=3, dec=4;

    int i, x;


    if(notation==1) {

        x=result[0];

        printf("%x", result[x]);

        for(i=1;i<x;i+=1) {

            printf("%08x", result[x-i]);

        }

        printf("\n");

    } else if(notation==2) {

    } else if(notation==3) {

    } else if(notation==4) {   

        convertResult(result, decimal);

        x=decimal[0];

        printf("%d", decimal[x]);

        for(i=1;i<x;i+=1) {

            printf("%08d", decimal[x-i]);

        }

        printf("\n");

    } else {

        printf("ERROR.\n");

    }

}

// 16진수의 표현으로 입력을 하는 것을 전제로 한다.

void convert(char a[], long long b[]) {

    int i, j;

    long long k, x;

    

    i=strlen(a)-1;

    x=0;

    j=1;

    k=1;

    while(i>=0) {

        if(a[i]<='9') {

            x+=k*(a[i]-'0');

        } else {

            x+=k*(a[i]-'a'+10);

        }

        k*=0x10;

        i-=1;

        if(k==0x100000000) {

            k=1;

            b[j]=x;

            j+=1;

            x=0;

        }

    }

    if(k>1) {

        b[j]=x;

        j+=1;

    }

    b[0]=j-1;

}

int main() {

    long long a[MAX]={1,};  // 피제수

    long long b[MAX]={1,};  // 제수

    long long q[MAX]={1,};  // 몫

    long long r[MAX]={1,};  // 나머지

    long long t[MAX]={1,};  // 임시

    long long s[MAX]={1,};  // 임시

    char m1[MAX8];

    char m2[MAX8];

    int i, x, z;


    while(1) {

        printf("1.덧셈 2.뺄셈 3.곱셈 4.나눗셈 0.마침 ");

        i=scanf("%d", &z);

        if(i<1) { 

            fflush(stdin);

            continue;

        }

        if(z==0) { 

            break; 

        } 

        printf("입력1   = ");

        scanf("%s", m1);

        printf("입력2   = ");

        scanf("%s", m2);


        // 입력된 수를 뒤집어 준다.

        convert(m1, a);

        convert(m2, b);


        if (z==1) {

            add(a, b, r);

        } else if(z==2) {

            sub(a, b, r);

        } else if(z==3) {

            mul(a, b, r);

        } else if(z==4) {

            div(a, b, q, r);

            printf("dividend= ");

            printResult(a, 4);

            printf("divider = ");

            printResult(b, 4);

            printf("몫      = ");

            printResult(q, 4);

            printf("나머지  = ");

            printResult(r, 4);

            mul(b, q, s);

            add(r, s, t);

            printf("원래값  = ");

            printResult(t, 1); 

        }

        // 결과를 출력해 준다.

        if(z<4) printResult(r, 1);

    }


    return 0;

}

'c·c++ > c 프로그래밍' 카테고리의 다른 글

절사평균 구하기  (0) 2013.08.21
ipconfig내용 캡쳐해서 가져오기  (0) 2013.08.21
큰수의 나눗셈 - 만진법  (0) 2013.07.31
큰수의 나눗셈 - 십진법  (0) 2013.07.29
확장된 애너그램 사전만들기  (0) 2013.07.24