/* * Je treba nastavit, aby se pouzil typ long long. */ #define _GNU_SOURCE /* * Knihovny. */ #include #include #include #include /* * Delky typu. */ /* int */ #if INT_MAX == 127 #define INT_SIZE 1 #elif INT_MAX == 32767 #define INT_SIZE 2 #elif INT_MAX == 2147483647 #define INT_SIZE 4 #elif INT_MAX == 9223372036854775807 #define INT_SIZE 8 #else Error: I cannot find length of the int type. #endif /* short */ #if SHRT_MAX == 127 #define SHRT_SIZE 1 #elif SHRT_MAX == 32767 #define SHRT_SIZE 2 #elif SHRT_MAX == 2147483647 #define SHRT_SIZE 4 #elif SHRT_MAX == 9223372036854775807 #define SHRT_SIZE 8 #else Error: I cannot find length of the short type. #endif /* long */ #if LONG_MAX == 127 #define LONG_SIZE 1 #elif LONG_MAX == 32767 #define LONG_SIZE 2 #elif LONG_MAX == 2147483647 #define LONG_SIZE 4 #elif LONG_MAX == 9223372036854775807 #define LONG_SIZE 8 #else Error: I cannot find length of the long type. #endif /* long long */ #ifdef LLONG_MAX #if LLONG_MAX == 127 #define LLONG_SIZE 1 #elif LLONG_MAX == 32767 #define LLONG_SIZE 2 #elif LLONG_MAX == 2147483647 #define LLONG_SIZE 4 #elif LLONG_MAX == 9223372036854775807 #define LLONG_SIZE 8 #else Error: I cannot find length of the long long type. #endif #endif /* * Najde typ a masku pro pouzivane bity slova pouziteho pro definici cisla. * Najde dvakrat vetsi typ a jeho masku. */ #if LONG_SIZE >= 2 * INT_SIZE typedef unsigned int word; #define WORD_BITS ( INT_SIZE * 8 ) typedef unsigned long int dword; #elif defined(LLONG_SIZE) && LLONG_SIZE >= 2 * INT_SIZE typedef unsigned int word; #define WORD_BITS ( INT_SIZE * 8 ) typedef unsigned long long int dword; #else typedef unsigned int word; #define WORD_BITS ( INT_SIZE * 8 / 2 ) typedef unsigned int dword; #endif #define DWORD_BITS ( WORD_BITS * 2 ) #define WORD_MASK ( ~((word)-1 << WORD_BITS - 1 << 1) ) #define DWORD_MASK ( ~((dword)-1 << DWORD_BITS - 1 << 1) ) /* * Chyba. */ static void error(const char *text, ...) { va_list list; va_start(list, text); vfprintf(stderr, text, list); exit(1); } /* * Delka cisla. * Pocet slov typu word, ktere obsahuje jedno dlouhe cislo. */ static int numberSize; /* * Nastavi numberSize. * Lze dokazat, ze staci (21/50 * digitsCount + 2 * INT_SIZE) bajtu. * Parametry: * digitsCount - Pocet pozadovanych desetinnych cislic. */ static void initNumberSize(int digitsCount) { numberSize = (digitsCount + 49) / 50 * 21 / (WORD_BITS / 8) + 4; /* Pridavame 4 misto 2, protoze word muze byt jen polovina integeru. */ } /* * Vytvori misto pro cislo. * Cislo obsahuje pouze mista za desetinnou teckou. * Nejvyssi slovo je prvni. */ static word *allocNumber() { word *data; data = (word *) calloc(numberSize, sizeof(word)); if ( data == NULL ) { /* chyba */ error("Malo pameti."); } return data; } /* * Uvolni pamet cisla. */ static void freeNumber(word *data) { free(data); } /* * Secte src1 a src2 a vysledek ulozi do dst. * Vraci prenos. */ static word addNumber(word *dst, const word *src1, const word *src2) { int cnt; word carry; carry = 0; cnt = numberSize; dst += cnt; src1 += cnt; src2 += cnt; while ( cnt-- ) { dword sum; /* posun ukazatele */ dst--; src1--; src2--; /* secti */ sum = (dword) *src1 + *src2 + carry; /* uloz */ *dst = (word) sum & WORD_MASK; carry = (word) (sum >> WORD_BITS); } return carry; } /* * Odecte src1 a src2 a vysledek (src1-src2) ulozi do dst. * Vraci vypujcku. */ static word subNumber(word *dst, const word *src1, const word *src2) { int cnt; word borrow; borrow = 0; cnt = numberSize; dst += cnt; src1 += cnt; src2 += cnt; while ( cnt-- ) { dword sum; /* posun ukazatele */ dst--; src1--; src2--; /* odecti */ sum = (dword) *src1 - *src2 - borrow & ((dword) WORD_MASK << 1 | 1); /* uloz */ *dst = (word) sum & WORD_MASK; borrow = (word) (sum >> WORD_BITS); } return borrow; } /* * Vynasobi src1 a src2 a vysledek ulozi do dst. * Vraci prenos. */ static word mulNumber(word *dst, const word *src1, word src2) { int cnt; word carry; carry = 0; cnt = numberSize; dst += cnt; src1 += cnt; while ( cnt-- ) { dword sum; /* posun ukazatele */ dst--; src1--; /* vynasob */ sum = (dword) *src1 * src2 + carry; /* uloz */ *dst = (word) sum & WORD_MASK; carry = (word) (sum >> WORD_BITS) & WORD_MASK; } return carry; } /* * Vydeli src1 a src2 a vysledek (src1/src2) ulozi do dst. * Chova se, jako by pred src1 bylo jeste jedno vyssi slovo s hodnotou high. */ static void divNumber(word *dst, const word *src1, word src2, word high) { int cnt; word carry; carry = high; cnt = numberSize; while ( cnt-- ) { dword divident; /* delenec */ divident = (dword) carry << WORD_BITS | *src1; /* vydel */ *dst = (word) (divident / src2) & WORD_MASK; carry = (word) (divident % src2) & WORD_MASK; /* posun ukazatele */ dst++; src1++; } } /* * Sets number value to 0. */ static void clearNumber(word *dst) { int cnt; cnt = numberSize; while ( cnt-- ) { *dst++ = 0; } } /* * Testuje, zda je cislo rovno nule. */ static int isZeroNumber(const word *dst) { int cnt; cnt = numberSize; while ( cnt-- ) { if ( *dst++ != 0 ) { /* cislo neni nula */ return 0; } } return 1; } /* * Do dst zapise atn(1/src). * Src be melo byt prirozene cislo. */ static void atnRev(word *dst, word src) { word i; word oldI; int minus; word *power; word *item; /* alloc numbers */ power = allocNumber(); item = allocNumber(); /* initialize variables */ i = 1; minus = 0; clearNumber(power); divNumber(power, power, src, 1); clearNumber(dst); /* cycle */ while ( 1 ) { divNumber(item, power, i, 0); if ( isZeroNumber(item) ) { break; } if ( minus ) { subNumber(dst, dst, item); } else { addNumber(dst, dst, item); } oldI = i; i = i + 2 & WORD_MASK; if ( i < oldI ) { /* preteklo to, chyba */ error("Prilis velky pocet mist.\n"); } minus = ! minus; divNumber(power, power, src * src, 0); } /* free numbers */ freeNumber(power); freeNumber(item); } /* * Vypocte pi do dst. */ static void calculatePi(word *dst) { word *tmp; /* alloc numbers */ tmp = allocNumber(); /* compute pi */ atnRev(dst, 2); atnRev(tmp, 3); addNumber(dst, dst, tmp); mulNumber(dst, dst, 4); /* free numbers */ freeNumber(tmp); } /* * Vytiskne cislo dst na dany pocet desetinnych mist. * Cislo se pri tisku smaze. * Tiskne pouze desetinna mista. */ static void printNumber(word *dst, int digitsCount) { word digit; while ( digitsCount-- ) { digit = mulNumber(dst, dst, 10); putchar((int) digit + '0'); } puts(""); } /* * Telo programu. * Jako parametr se zada pocet pozadovanych cislic. */ int main(int argc, const char **argv) { int digitsCount; word *pi; /* pocet parametru */ if ( argc != 2 ) { /* chybny pocet parametru */ error("Volani: %s \nVypocte desinna mista pi s maximalni chybou +/-1 posledni cislice.\n", argv[0]); } /* cte pocet cislic */ if ( sscanf(argv[1], "%d", &digitsCount) != 1 || digitsCount < 0 ) { /* chybny pocet cislic */ error("Pocet cislic musi byt nezaporne cele cislo.\n"); } /* inicializuje delku cisla */ initNumberSize(digitsCount); /* alokuje misto */ pi = allocNumber(); /* vypocet pi */ calculatePi(pi); /* vytiskne pi */ printNumber(pi, digitsCount); /* uvolni misto */ freeNumber(pi); return 0; }