기초적인 BigFloat의 수학적 원리는 다음과 같아요~
십진법은 위와 같은 규칙으로 표현됩니다. 십진법에서의 덧셈은 각 자리수에 해당하는 숫자들끼리 더해서 받아올림을 함으로써 연산됩니다. 이를 32비트 컴퓨터에서 효율적으로 구현하기 위해서는 10진법보다는진법을 사용하는것이 좋겠지요.
이런 모양으로 실수를 표현할수 있을겁니다. 일단 이번 연재의 궁극적인 목표는 Pi값의 계산이니만큼 정수부분이 무지막지하게 큰 정수를 표현해야할 필요는 없습니다. a, b부분은 쳐내고, 다음과 같은 모양으로 표현하는게 괜찮겠네요.
a0은 정수부분을 표현하는 데이터로, a1부터는 소수점 이하를 표현하는 데이터로 활용하면 될것입니다. 자 이제 이걸 바탕으로 우리의 BigFloat자료형이 될 RealReal구조체를 짜봅시다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
typedef unsigned char UINT8; typedef unsigned short UINT16; typedef unsigned long UINT32 ; typedef unsigned long long UINT64 ; typedef struct tagRealReal { UINT32 * frac; UINT32 alloced; UINT32 size; UINT32 sign; } RR; typedef RR* PRR; #define SIZE_TO_BYTE(size) ((size)*sizeof(UINT32)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b)) PRR RR_create( UINT32 size); //RealReal 데이터 타입을 생성해주는 함수 void RR_delete(PRR rr); // 다 사용한 RealReal타입을 소멸시켜주는 함수 void RR_move(PRR dest, PRR src); //src의 값을 dest에게 복사하는 함수 int RR_init(PRR rr, double value); //RR을 value값으로 초기화시켜주는 함수 void RR_printHex(PRR rr, FILE * f); //RR을 16진수로 출력하는 함수 |
frac이 a0, a1, a2 등등을 표현할 배열.
alloced는 frac에 할당된 배열의 길이를 나타낼 것입니다.
size는 우리가 실제로 사용할 frac의 길이를 말하구요. (굳이 alloced와 size를 나눈 이유는 STL vector에서 reserved와 비슷한 기능을 후에 구현하려고 하기 때문이랍니다.)
sign은 양수인지 음수인지 나타내는 부호입니다.
그 밑에 이번에 구현해볼 함수들이 선언되어있습니다~!
하나씩 살펴볼까요
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
PRR RR_create( UINT32 size) { PRR rr = (PRR) malloc ( sizeof (RR)); rr->alloced = MAX(size, 4); //size는 최소 4. 4보다 작으면 그냥 double을 쓰는거랑 별차이점이 없겠죠ㅋ rr->frac = ( UINT32 *) malloc (SIZE_TO_BYTE(rr->alloced)); memset (rr->frac, 0, SIZE_TO_BYTE(rr->alloced)); // 값을 0으로 초기화시켜주고 rr->size = rr->alloced; rr->sign = 0; // 부호는 +로 초기화. +를 0, -를 1이라고 약속합시다! return rr; } void RR_delete(PRR rr) { free (rr->frac); free (rr); //소쿨할정도로 심플한 해제 } void RR_move(PRR dest, PRR src) // 복사함수입니다. { dest->sign = src->sign; //먼저 부호부터 복사 if (dest->size > src->size) { /* dest->size가 클 경우는 src->size만큼만 복사하고, 남는 공간은 0으로 초기화시켜줘야겠지요. */ memcpy (dest->frac, src->frac, SIZE_TO_BYTE(src->size)); UINT32 l = dest->size - src->size; memset (dest->frac + src->size, 0, SIZE_TO_BYTE(l)); } else { /* src->size가 dest->size보다 큰 경우는 dest->size만큼만 복사하도록. */ memcpy (dest->frac, src->frac, SIZE_TO_BYTE(dest->size)); } } int RR_init(PRR rr, double value) { if (value > 0xFFFFFFFF) return -1; /* value가 32비트 정수 최대치를 넘으면 저장못하겠죠. 우리는 정수부분에 32비트만 쓰기로 했으니깐요. */ if (value < 0) { rr->sign = 1; value = -value; /* 부호가 음수인 경우엔 sign을 1로 설정하고, value에는 절대값만 남깁니다. */ } else { rr->sign = 0; } rr->frac[0] = ( UINT32 )value; //정수부분만 취해서 frac[0]에 저장 value -= rr->frac[0]; //소수부분만 남기고 value *= 4294967296.; // 2^32를 곱해서 rr->frac[1] = ( UINT32 )value; value -= rr->frac[1]; value *= 4294967296.; rr->frac[2] = ( UINT32 )value; memset (rr->frac + 3, 0, SIZE_TO_BYTE(rr->size - 3)); return 0; } void RR_printHex(PRR rr, FILE * f) { if (rr->sign) fprintf (f, "-" ); fprintf (f, "%x." , rr->frac[0]); for ( UINT32 i = 1; i < rr->size; ++i) { fprintf (f, "%08x" , rr->frac[i]); } } |
어때요? 참 쉽죠..? (아직까지는ㅋ)
printHex함수는 16진수로 RealReal포맷을 출력해줍니다. 굳이 10진수 대신 16진수로 표현하는 이유는 10진수로 표현하려면 진수변환을 해야하는데, 아직 그러기에는 먼저 구현해야할것이 많기 때문이지요.
BigFloat로 Pi를 구해보자-4. 나눗셈은 어려워 (나눗셈 구현 알고리즘) (0) | 2012.10.24 |
---|---|
BigFloat로 Pi를 구해보자-3. 곱셈 구현 + 10진수로 출력하기 (0) | 2012.10.18 |
BigFloat로 Pi를 구해보자-2. 덧셈, 뺄셈 구현하기 (0) | 2012.10.16 |
BigFloat로 Pi를 구해보자-0. 시작하며 (1) | 2012.10.16 |
드디어 오류를 잡아냈다. (2) | 2010.09.11 |
BigInteger를 구현해보자 - 10. 10진수로 입력받기 (5) | 2009.04.30 |
댓글 영역