Co to jest CHAR_BIT?

93

Cytując kod do obliczenia całkowitej wartości bezwzględnej (abs) bez rozgałęziania z http://graphics.stanford.edu/~seander/bithacks.html :

int v;           // we want to find the absolute value of v
unsigned int r;  // the result goes here 
int const mask = v >> sizeof(int) * CHAR_BIT - 1;

r = (v + mask) ^ mask;

Opatentowana odmiana:

r = (v ^ mask) - mask;

Co to jest CHAR_BITi jak z niego korzystać?

dato datuashvili
źródło

Odpowiedzi:

0

Należy pamiętać, że ten kod zależy od zdefiniowanego w implementacji zachowania prawego przesunięcia bitu na typach podpisanych. gcc obiecuje, że zawsze będzie zachowywał się rozsądnie (rozszerzenie bitu znaku), ale ISO C pozwala implementacji na wypełnienie górnych bitów zerami.

Jeden sposób na obejście tego problemu:

#ifdef HAVE_SIGN_EXTENDING_BITSHIFT
int const mask = v >> sizeof(int) * CHAR_BIT - 1;
#else
int const mask = -((unsigned)v >> sizeof(int) * CHAR_BIT - 1);
#endif

Twój Makefilelub config.hitp. Można zdefiniować HAVE_SIGN_EXTENDING_BITSHIFTw czasie kompilacji w zależności od platformy.

R .. GitHub PRZESTAŃ POMÓC LODOWI
źródło
122
Nie rozumiem, jak to może być akceptowana odpowiedź, ponieważ nie odpowiada na pytanie, mimo że jest to bardzo interesujący komentarz.
qdii
16
@Mauris: Ktoś zredagował pytanie i dodał pytanie podrzędne do tytułu pytania. Oryginalny tytuł był wprawdzie okropny, ale pytanie OP dotyczyło tego, jak działa cytowany kod bitowy, a „nie działa, przynajmniej nie przenośnie, a oto dlaczego” jest przydatną odpowiedzią.
R .. GitHub PRZESTAŃ POMÓC W LODZIE
12
Oh rozumiem. Niestety, to pytanie pojawia się bardzo wysoko w wynikach wyszukiwania Google dla zapytania „Co to jest CHAR_BIT?” , nawet jeśli nie było to pierwotne pytanie. :( Biorąc pod uwagę twoje wyjaśnienie, rozumiem, dlaczego napisałeś tę odpowiedź, ale dla potomnych może być bardziej przydatne albo (a) usunięcie odpowiedzi i przepisanie jej jako komentarz do pytania, tak aby @ AraK pojawił się na górze, albo (b) zredaguj swoją odpowiedź tak, aby odpowiadała aktualnemu tytułowi pytania
Lynn
1
Ze względu na różnicę w intencji (intencjach) między pierwotnym pytaniem PO a jego interpretacją wydawcy, wydaje się, że charakter pierwotnego wniosku został mimowolnie zmieniony. Chociaż oba pytania (oryginalne i zredagowane) mają wartość, należy zająć się tą rozbieżnością. Teraz pytam: czy ta odpowiedź może zostać dodana do wiki? To prawdopodobnie pomogłoby ludziom, którzy szukają tego typu informacji, chociaż nie dotyczy to pierwotnego pytania. Po tym pytanie można było ponownie zredagować, aby pasowało do pierwotnej prośby dato datuashvili. Tylko zaniepokojony czytelnik ...
2
Właśnie spojrzałem na historię tego pytania i pierwotne pytanie nigdzie nie pytało, jak działa kod. Pytanie, które redaktor awansował do tytułu, jest tam jedynym faktycznym pytaniem.
plugwash
230

CHAR_BITjest liczbą bitów w char. Obecnie prawie wszystkie architektury używają 8 bitów na bajt, ale nie zawsze tak jest. Niektóre starsze maszyny miały 7-bitowe bajty.

Można go znaleźć w <limits.h>.

AraK
źródło
3
Niektóre procesory DSP mają 10 lub więcej bitów.
Juri Robl
64
C wymaga CHAR_BIT>=8i dopuszcza znacznie większe wartości dla DSP, które mają tylko jeden rozmiar typu, często 32-bitowy. POSIX wymaga CHAR_BIT==8. Ogólnie rzecz biorąc, można założyć, że każda architektura zorientowana na serwer lub zorientowana na interaktywne użytkowanie dla wielu użytkowników / wielozadaniowości z możliwością podłączenia do Internetu lub wymiany danych tekstowych ze światem zewnętrznym CHAR_BIT==8.
R .. GitHub PRZESTAŃ POMÓC NA LODZIE
6
@caf: Nie, chodzi o to, że C99 wymaga typów int8_ti uint8_tistnienia. Tak więc istnieje typ szerokości 8. Ponieważ sizeofkażdy typ musi być zgodny z, w sizeof charrzeczywistości sizeof int8_tmusi wynosić 1. Więc CHAR_BIT == 8. Pisałem coś wokół tego obeservation tutaj: gustedt.wordpress.com/2010/06/01/how-many-bits-has-a-byte
Jens Gustedt
22
@Jens Gustedt: Proszę zacytować sekcję w specyfikacji C99. Spośród typów całkowitych o dokładnej szerokości specyfikacja C99 mówi: „Te typy są opcjonalne”. (7.18.1.1/3) Wymagane są jednak typy szerokości minimalnej i największej.
jamesdlin
3
@jamesdlin & caf: przepraszam, że pomieszałem. tak, wymóg, do którego się odniosłem, faktycznie pochodzi z POSIX dla stdint.h. Tak więc jest to wymagane i jest również oznaczone jako rozszerzenie standardu ISO C , bez odwoływania się do konkretnej wersji tej normy. Mój błąd.
Jens Gustedt
2

Próbuje odpowiedzieć zarówno na pytanie jawne (co to jest CHAR_BIT), jak i na niejawne (jak to działa) w pierwotnym pytaniu.


Znak w C i C ++ reprezentuje najmniejszą jednostkę pamięci, do której program C może adresować *

CHAR_BIT w C i C ++ reprezentuje liczbę bitów w znaku. Zawsze musi wynosić co najmniej 8 ze względu na inne wymagania dotyczące typu znaku. W praktyce we wszystkich nowoczesnych komputerach ogólnego przeznaczenia wynosi dokładnie 8, ale niektóre systemy historyczne lub specjalistyczne mogą mieć wyższe wartości.

Java nie ma odpowiednika CHAR_BIT ani sizeof, nie ma takiej potrzeby, ponieważ wszystkie typy pierwotne w Javie mają stały rozmiar, a wewnętrzna struktura obiektów jest nieprzejrzysta dla programisty. Jeśli tłumaczysz ten kod na Javę, możesz po prostu zamienić „sizeof (int) * CHAR_BIT - 1” na stałą wartość 31.

W tym konkretnym kodzie jest używany do obliczenia liczby bitów w int. Należy pamiętać, że to obliczenie zakłada, że ​​typ int nie zawiera żadnych bitów wypełniających.

Zakładając, że twój kompilator zdecyduje się na rozszerzenie ze znakiem na przesunięciach bitowych liczb ze znakiem i zakładając, że twój system używa reprezentacji dopełnienia 2s dla liczb ujemnych, oznacza to, że "MASKA" będzie równe 0 dla wartości dodatniej lub zerowej i -1 dla wartości ujemnej.

Aby zanegować liczbę uzupełnienia do dwójek, musimy wykonać bitowe nie, a następnie dodać jeden. Równocześnie możemy odjąć jeden, a następnie zanegować bitowo.

Ponownie zakładając, że reprezentacja dopełnienia dwójki -1 jest reprezentowana przez wszystkie jedynki, więc wyłączność lub z -1 jest równoważne negacji bitowej.

Więc kiedy v jest równe zero, liczba jest pozostawiana sama, gdy v wynosi jeden, jest negowana.

Należy być świadomym tego, że przepełnienie podpisu w C i C ++ jest niezdefiniowanym zachowaniem. Tak więc użycie tej implementacji ABS na najbardziej ujemnej wartości prowadzi do nieokreślonego zachowania. Można to naprawić, dodając rzutowania, tak aby ostatnia linia programu była oceniana w unsigned int.

* Która zwykle, ale nie zawsze jest taka sama, jak najmniejsza jednostka pamięci, którą sprzęt może adresować. Implementacja może potencjalnie łączyć wiele jednostek pamięci adresowalnej sprzętowo w jedną jednostkę pamięci adresowalnej programowo lub podzielić jedną jednostkę pamięci adresowalnej sprzętowo na wiele jednostek pamięci adresowalnej programowo.

plugwash
źródło