Wiem, że standardy C i C ++ pozostawiają wiele aspektów implementacji języka tylko dlatego, że gdyby istniała architektura o innych cechach, byłoby bardzo trudne lub niemożliwe napisanie dla niej kompilatora zgodnego ze standardami.
Wiem, że 40 lat temu każdy komputer miał swoją własną, unikalną specyfikację. Jednak nie znam żadnej architektury używanej dzisiaj, gdzie:
CHAR_BIT != 8
signed
nie jest uzupełnieniem do dwóch (słyszałem, że Java miała z tym problemy).- Liczba zmiennoprzecinkowa nie jest zgodna z IEEE 754 (Edycja: miałem na myśli „nie w kodowaniu binarnym IEEE 754”).
Pytam, ponieważ często wyjaśniam ludziom, że to dobrze, że C ++ nie narzuca żadnych innych aspektów niskiego poziomu, takich jak typy o ustalonej wielkości † . Jest to dobre, ponieważ w przeciwieństwie do `` innych języków '' sprawia, że twój kod jest przenośny, gdy jest używany poprawnie (Edycja: ponieważ można go przenieść na więcej architektur bez konieczności emulacji niskopoziomowych aspektów maszyny, takich jak np. Arytmetyka dopełniająca do dwóch na architekturze znak + wielkość) . Ale czuję się źle, że sam nie mogę wskazać żadnej konkretnej architektury.
A więc pytanie brzmi: jakie architektury wykazują powyższe właściwości?
† uint*_t
s są opcjonalne.
źródło
Odpowiedzi:
Spójrz na to
Serwery Unisys ClearPath Dorado
oferując kompatybilność wsteczną dla osób, które nie migrowały jeszcze całego oprogramowania Univac.
Kluczowe punkty:
CHAR_BIT == 9
Nie wiem, czy oferują kompilator C ++, ale mogliby .
A teraz pojawił się link do najnowszego wydania ich podręcznika C:
Podręcznik programowania kompilatora Unisys C.
Rozdział 4.5 zawiera tabelę typów danych z 9, 18, 36 i 72 bitami.
źródło
char*
ivoid*
muszą być tej samej wielkości i wystarczająco duże, aby pomieścić dowolny inny wskaźnik. Reszta zależy od realizacji.sizeof(int*) == 2
, ale dalekie wskaźniki miały również 16-bitowy selektor, więcsizeof(void*) == 4
.sizeof(int*) != sizeof(char*)
tutaj: oba mają 36 bitów. Ale selektor bajtów wchar*
znajduje się na bitach wyższego rzędu i jest ignorowanyint*
. (Użyłem jednak innych maszyn, gdzie `sizeof (char *)> sizeof (int *).)void*
) zawsze miały ten sam rozmiar. (Oczywiście nie można było przekonwertować wskaźnika funkcji navoid*
, ponieważvoid*
może być mniejszy. Ale zgodnie ze standardem nie można tego zrobić również dzisiaj.)Żadne z twoich założeń nie odnosi się do komputerów mainframe. Na początek nie znam komputera mainframe, który używa standardu IEEE 754: IBM używa liczby zmiennoprzecinkowej o podstawie 16, a oba komputery mainframe Unisys używają podstawy 8. Maszyny Unisys są nieco wyjątkowe pod wieloma innymi względami: Bo wspomniał o 2200 architektura, ale architektura MPS jest jeszcze dziwniejsza: 48-bitowe słowa tagowane. (To, czy słowo jest wskaźnikiem, czy nie, zależy od bitu w słowie). Reprezentacje liczbowe są zaprojektowane w taki sposób, że nie ma rzeczywistego rozróżnienia między arytmetyką zmiennoprzecinkową i całkową: zmiennoprzecinkowa ma podstawę 8; nie wymaga normalizacji iw przeciwieństwie do wszystkich innych widzianych przeze mnie liczb zmiennoprzecinkowych, umieszcza ułamek dziesiętny po prawej stronie mantysy, a nie po lewej, i używa wartości ze znakiem jako wykładnika (oprócz mantysy). Z wynikami, że całkowita wartość zmiennoprzecinkowa ma (lub może mieć) dokładnie taką samą reprezentację bitową jak liczba całkowita ze znakiem. I nie ma instrukcji arytmetycznych zmiennoprzecinkowych: jeśli wykładniki obu wartości są równe 0, instrukcja wykonuje arytmetykę całkową, w przeciwnym razie wykonuje arytmetykę zmiennoprzecinkową. (Kontynuacja filozofii tagowania w architekturze.) Co oznacza, że podczas
int
może zajmować 48 bitów, 8 z nich musi wynosić 0, inaczej wartość nie będzie traktowana jako liczba całkowita.źródło
Pełna zgodność ze standardem IEEE 754 jest rzadkością w implementacjach zmiennoprzecinkowych. A osłabienie specyfikacji w tym zakresie pozwala na wiele optymalizacji.
Na przykład obsługa subnormy różni się między x87 i SSE.
Optymalizacje, takie jak łączenie mnożenia i dodawania, które były oddzielne w kodzie źródłowym, również nieznacznie zmieniają wyniki, ale są dobrą optymalizacją na niektórych architekturach.
Lub na x86 ścisła zgodność ze standardem IEEE może wymagać ustawienia pewnych flag lub dodatkowych transferów między rejestrami zmiennoprzecinkowymi i normalną pamięcią, aby wymusić użycie określonego typu zmiennoprzecinkowego zamiast wewnętrznych 80-bitowych wartości zmiennoprzecinkowych.
Niektóre platformy nie mają żadnych pływaków sprzętowych i dlatego muszą emulować je w oprogramowaniu. Niektóre wymagania IEEE 754 mogą być drogie do wdrożenia w oprogramowaniu. W szczególności problemem mogą być zasady zaokrąglania.
Mój wniosek jest taki, że nie potrzebujesz egzotycznych architektur, aby znaleźć się w sytuacjach, w których nie zawsze chcesz zagwarantować ścisłą zgodność z IEEE. Z tego powodu niewiele języków programowania gwarantuje ścisłą zgodność z IEEE.
źródło
long double
mógłby być użytecznym i długowiecznym typem, ponieważ jedynym prawdziwym problemem było to, że źle działaprintf
. Fakt, że rozszerzona podwójna przechowuje wiodącą 1 wyraźnie przyspiesza obliczenia w systemach innych niż FPU, a także eliminowałby potrzebę specjalnej obsługi denormali w dowolnym kontekście innym niż konwersje do / z innych typów. Szkoda, że Cprintf
wszystko zepsuło.Znalazłem ten link zawierający listę niektórych systemów, w których
CHAR_BIT != 8
. ZawierająI oczywiście pojawia się pytanie dotyczące przepełnienia stosu: jakie platformy mają coś innego niż 8-bitowe znaki
Jeśli chodzi o systemy z dopełnieniem innym niż dwa, jest ciekawa lektura na temat comp.lang.c ++. Moderated . Podsumowując: istnieją platformy, które mają swoją reprezentację dopełnienia lub znaku i wielkości.
źródło
CHAR_BIT=32
, a Texas Instruments DSP z TMS32F28xx maCHAR_BIT=16
. GCC 3.2 dla PDP-10 maCHAR_BIT=9
. Myślę, że S / 360 może mieć też nie-8-bitowy znak.CHAR_BITS
jest to częściowy duplikat.Jestem prawie pewien, że systemy VAX są nadal w użyciu. Nie obsługują zmiennoprzecinkowych IEEE; używają własnych formatów. Alpha obsługuje formaty zmiennoprzecinkowe VAX i IEEE.
Maszyny wektorów Cray, takie jak T90, również mają swój własny format zmiennoprzecinkowy, chociaż nowsze systemy Cray używają IEEE. (T90, którego używałem, został wycofany z eksploatacji kilka lat temu; nie wiem, czy któryś z nich jest nadal w użyciu.)
T90 miał również / ma kilka interesujących reprezentacji wskaźników i liczb całkowitych. Natywny adres może wskazywać tylko na słowo 64-bitowe. Kompilatory C i C ++ miały CHAR_BIT == 8 (konieczne, ponieważ działał Unicos, odmiana Uniksa i musiał współpracować z innymi systemami), ale natywny adres mógł wskazywać tylko na słowo 64-bitowe. Wszystkie operacje na poziomie bajtów zostały zsyntetyzowane przez kompilator i a
void*
lubchar*
zapisane przesunięcie bajtowe w trzech najwyższych bitach słowa. Myślę, że niektóre typy liczb całkowitych miały bity wypełnienia.Innym przykładem są komputery mainframe IBM.
Z drugiej strony, te konkretne systemy nie muszą koniecznie wykluczać zmian w standardzie językowym. Cray nie wykazywał żadnego szczególnego zainteresowania aktualizacją swojego kompilatora C do C99; przypuszczalnie to samo dotyczy kompilatora C ++. To może być uzasadnione, aby zaostrzyć wymagania dla hostowanych implementacjach, takie jak wymóg CHAR_BIT == 8 formacie IEEE zmiennoprzecinkowych jeśli nie pełne semantyki i 2's-dopełniacza bez bitów wypełniających dla podpisanych liczb całkowitych. Stare systemy mogą nadal obsługiwać wcześniejsze standardy językowe (C90 nie umarł, gdy pojawił się C99), a wymagania mogą być luźniejsze dla implementacji wolnostojących (systemów wbudowanych), takich jak procesory DSP.
Z drugiej strony, mogą istnieć dobre powody, dla których przyszłe systemy będą robić rzeczy, które dziś byłyby uważane za egzotyczne.
źródło
unsigned
typów całkowitych będą dużym problemem , podczas gdy arytmetyka ze znakiem będzie w porządku.CHAR_BITS
Według kodu źródłowego GCC :
CHAR_BIT
jest16
bity 1750a , dsp16xx architekturach.CHAR_BIT
to24
bity dla architektury dsp56k .CHAR_BIT
to32
bity dla architektury c4x .Możesz łatwo znaleźć więcej, wykonując:
lub
jeśli
CHAR_TYPE_SIZE
jest odpowiednio zdefiniowana.Zgodność z IEEE 754
Jeśli architektura docelowa nie obsługuje instrukcji zmiennoprzecinkowych, gcc może wygenerować oprogramowanie awaryjne, które nie jest domyślnie zgodne ze standardem. Co więcej,
-funsafe-math-optimizations
można użyć specjalnych opcji (takich jak witch również wyłącza zachowywanie znaków dla zer).źródło
Reprezentacja binarna IEEE 754 była do niedawna rzadkością na procesorach graficznych, patrz Paranoja zmiennoprzecinkowa GPU .
EDYCJA: w komentarzach pojawiło się pytanie, czy zmiennoprzecinkowy GPU ma znaczenie dla zwykłego programowania komputerowego, niezwiązanego z grafiką. O tak! Większość obliczeń przemysłowych o wysokiej wydajności odbywa się obecnie na procesorach graficznych; lista obejmuje sztuczną inteligencję, eksplorację danych, sieci neuronowe, symulacje fizyczne, prognozę pogody i wiele więcej. Jedno z linków w komentarzach pokazuje dlaczego: przewaga zmiennoprzecinkowa rzędu wielkości GPU.
Kolejna rzecz, którą chciałbym dodać, która jest bardziej odpowiednia dla pytania OP: co ludzie robili 10-15 lat temu, kiedy zmiennoprzecinkowy GPU nie był IEEE i kiedy nie było API, takiego jak dzisiejszy OpenCL lub CUDA do programowania GPU? Wierzcie lub nie, pionierom obliczeń GPU udało się zaprogramować układy GPU bez interfejsu API, aby to zrobić ! Spotkałem jednego z nich w swojej firmie. Oto, co zrobił: zakodował dane, których potrzebował do obliczenia, jako obraz z pikselami reprezentującymi wartości, nad którymi pracował, a następnie użył OpenGL do wykonania potrzebnych operacji (takich jak „rozmycie gaussowskie”, aby przedstawić splot z rozkładem normalnym itp.) i zdekodował wynikowy obraz z powrotem do tablicy wyników. A to wciąż było szybsze niż użycie procesora!
Takie rzeczy skłoniły NVidię do ostatecznego dostosowania swoich wewnętrznych danych binarnych do IEEE i wprowadzenia interfejsu API zorientowanego na obliczenia, a nie na manipulację obrazem.
źródło
int f(int n) { return n <= 1 ? 1 : n * f(n-1); }
w CUDA? Jeśli nie, to GPU nie są istotne dla tego pytania (które dotyczy komitetów C i C ++).