2010-12-28 13 views
11

Problem polega na tym, aby utworzyć rodzaj dużej biblioteki całkowitej. Chcę uczynić to zarówno na platformie, jak i tak szybko, jak to możliwe. Oznacza to, że powinienem próbować robić matematykę z tak dużymi typami danych, jakie są natywnie obsługiwane w systemie.Znajdź największy rodzimy typ liczby całkowitej na bieżącej platformie.

Nie chcę wiedzieć, czy kompiluję dla systemu 32- lub 64-bitowego; wszystko, czego potrzebuję, to sposób na utworzenie 64-bitowego lub 32-bitowego pliku lub dowolnej liczby całkowitej w oparciu o to, co jest największym dostępnym. Będę używał sizeof, aby zachowywać się inaczej w zależności od tego, co to jest.

Oto kilka możliwych rozwiązań i ich problemy:

Użyj opcji sizeof (void *): Daje wielkość wskaźnika do pamięci. Jest możliwe (choć jest mało prawdopodobne), że system może mieć większe wskaźniki do pamięci, niż jest w stanie wykonać matematykę lub odwrotnie.

Zawsze używaj długo: Podczas gdy prawdą jest, że na wielu platformach długie liczby całkowite są 4 bajty lub 8 bajtów w zależności od architektury (mój system jest jednym z takich przykładów), niektóre kompilatory implementują długie liczby całkowite jako 4 bajty, nawet na 64 systemy bitowe.

Zawsze używaj długo długo: W wielu systemach 32-bitowych jest to 64-bitowa liczba całkowita, która może nie być tak wydajna (choć prawdopodobnie bardziej wydajna niż jakikolwiek kod, który piszę). Prawdziwy problem polega na tym, że może nie być obsługiwany w ogóle na niektórych architekturach (takich jak ten, który zasila mój odtwarzacz mp3).

Dla podkreślenia, mój kod nie obchodzi, jaki jest rzeczywisty rozmiar liczby całkowitej po wybraniu (zależy od sizeof() dla czegokolwiek, gdzie rozmiar ma znaczenie). Chcę tylko wybrać typ liczby całkowitej, która spowoduje, że mój kod będzie najbardziej wydajny.

+0

Pierwszą rzeczą, która przychodzi mi do głowy, jest ustalenie wielkości rejestrów procesora w pewien sposób ... – BlackBear

+0

czy szukałeś odpowiedzi w istniejących bibliotekach Bignum? http://sourceforge.net/projects/bignlibacbignum/ – mth

+0

Być może próbując coś w nich przenieść, dodaj coś i sprawdź flagę przepełnienia. – BlackBear

Odpowiedz

6

Jeśli naprawdę chcesz mieć rodzimy rozmiar, użyłbym size_t, ptrdiff_t lub intptr_t i uintptr_t. W każdym niepatologicznym systemie wszystkie będą miały rozmiar natywnego słowa.

Z drugiej strony, są z pewnością korzyści pod względem prostoty, aby zawsze pracować z ustalonym rozmiarem, w takim przypadku po prostu użyłbym int32_t lub uint32_t.Powodem, dla którego mówię, że jest to prostsze, jest to, że często kończy się potrzeba znajomości rzeczy takich jak "największa moc 10 pasująca do typu" (dla konwersji dziesiętnej) i inne stałe, których nie można łatwo wyrazić jako wyrażenia stałe w kategoriach typu użyłeś. Jeśli wybierzesz tylko określoną liczbę bitów, możesz również naprawić wygodne stałe (na przykład 1000000000 w moim przykładzie). Oczywiście robiąc to w ten sposób, poświęcasz trochę wydajności na wyższych systemach. Można zastosować podejście odwrotne i użyć większego stałego rozmiaru (64 bity), który byłby optymalny w systemach wyższej klasy i przyjąć, że kod kompilatora dla 64-bitowej arytmetyki na 32-bitowych komputerach będzie co najmniej tak szybki jak Twój kod binarny obsługuje 2 32-bitowe słowa, w tym przypadku nadal jest optymalny.

+0

Dzięki, pierwsza część odpowiedzi jest właśnie tym, czego szukam. – Collin

+0

Z drugiej strony, myślę, że i tak prawdopodobnie będę używał GMP. – Collin

+1

Ostrzeżenie: 'ptrdiff_t' ma wystarczającą wielkość, aby odjąć wskaźnik, ale nikt nie powiedział, że nie może być większy (chociaż często nie widzisz 64-bitowego' ptrdiff_t', gdy 'size_t' jest 32-bitowe, nawet jeśli oznacza to, że różnica w więcej niż połowie przestrzeni adresowej nie może być przechowywana w 'ptrdiff_t'). Uważaj na standardy, na których polegasz. – Mehrdad

4

Najlepszym sposobem nie jest poleganie na automatycznym wykrywaniu, ale na ukierunkowaniu konkretnych kompilatorów za pomocą zestawu instrukcji #if/#else w celu wybrania typu, który testowałeś i wiesz, że jest optymalny.

+0

Z awarią do uintptr_t, być może, jeśli kompilator nie może zostać wykryty. – Charles

0

Oto how we did it w bsdnt:

#if ULONG_MAX == 4294967295U 

typedef uint32_t word_t; 
typedef unsigned int dword_t __attribute__((mode(DI))); 
#define WORD_BITS 32 

#else 

typedef uint64_t word_t; 
typedef unsigned int dword_t __attribute__((mode(TI))); 
#define WORD_BITS 64 

#endif 

Jeśli jest to interesujące, facet, który zainicjował projekt napisał blog na pisanie bibliotek bignum.

GMP/MPIR jest znacznie bardziej skomplikowany; gmp-h.in się gmp.h post-konfiguracyjny, który określa to:

#define GMP_LIMB_BITS      @[email protected] 

Krótko mówiąc, długość jest ustawiony jako część procesu tworzenia którymi współpracuje za pośrednictwem config.guess (tj autotoolsów).

+0

co jeśli ULONG_MAX to 2^48 - 1? –

+0

Oszustwne hacki specyficzne dla gcc .... –

+0

@R Rzeczywiście. To rzeczywiście jest praca w toku. Sugeruję, żebyś nie szukał "__attribute__" w źródle GMP, to ... –

0

Korzystanie z int_fast32_t z stdint.h wydaje się być opcją, chociaż jesteś na łasce tych, którzy decydują, co oznacza "szybki".

+1

Jeśli nie ma kary za dostęp 32-bitowy, jak na x86_64, 'int_fast32_t' powinien być typu 32-bitowego, ale wykonującego arytmetyczną bignum w 64-bitowych jednostkach byłoby z pewnością lepsze ... –

+1

Możesz mieć rację, ale na mojej maszynie x86_64 (Ubuntu 10.10) jest 64-bitowa. Spodziewam się, że będzie to korelować z szerokością głównych rejestrów danych maszyny, ale nie mam na to żadnych dowodów. – ergosys

Powiązane problemy