2015-10-21 31 views
7

Rozważmy następujący kod:Czy operacje bitowe na podpisanych typach całek są dobrze zdefiniowane?

using integer = int; // or any other fundamental integral type 
using unsigned_integer = typename std::make_unsigned<integer>::type; 
constexpr integer bits = std::numeric_limits<unsigned_integer>::digits; 
integer value = -42; // or any value 
integer mask = static_cast<integer>(1)<<static_cast<integer>(bits-1); 
bool result_and = value & mask; 
bool result_or = value | mask; 
bool result_xor = value^mask; 

Zastanawiam się, jak dobrze te operacje są zdefiniowane zgodnie z normą. Czy mam gwarancję, że otrzymam takie same wyniki na wszystkich architekturach? Na pewno działam na bicie znaku na wszystkich architekturach, gdzie ten bit znaku jest 0 dla liczb dodatnich i 1 dla liczb ujemnych?

+0

Zobacz także [Jaki jest wynik b & b] (http://stackoverflow.com/q/29394518/1708801), który jest blisko spokrewniony, ale nie jest duplikatem. –

Odpowiedz

3

Wyniki bitowe i bitowe lub bitowe xor są obecnie niedookreślone w standardzie, w szczególności termin bitowy nigdy nie jest zdefiniowany. Mamy defect report 1857: Additional questions about bits który obejmuje ten problem i mówi:

Specyfikacja bitowego operacji w 5.11 [expr.bit.and] 5,12 [expr.xor] i 5,13 [expr.or] wykorzystuje niezdefiniowane termin "bitowy" w opisie operacji, bez określania, czy jest to wartość lub reprezentacja obiektu, która jest w widoku.

Do rozwiązania tego może być określenie „bit” (który jest poza obecnie nieokreślony, C++) jako wartość danej mocy 2.

i rozdzielczości:

CWG postanowiła zmienić definicje operacji, aby uniknąć odniesień do bitów, oddzielając większe pytania definiowania "bitów" i tym podobnych do wydania 1943 w celu dalszego rozważenia pod kątem .

Co zaowocowało konsolidacją defect report 1943: Unspecified meaning of “bit”.

Wynik lewego przesunięcia podpisanego typu będzie zależał od podstawowej reprezentacji. Widzimy to od defect report 1457: Undefined behavior in left-shift co sprawiło, że dobrze zdefiniowana w lewo przesunąć do bitu znaku i mówi:

Obecne brzmienie 5,8 [expr.shift] pkt 2 sprawia, że ​​zachowanie niezdefiniowane stworzyć MOST ujemna danego typu przez lewo przesuwanie (podpisany) 1 do bitu znaku, choć nie jest to niezwykle zrobione i działa poprawnie na większości (dwójkami-dopełniacza) architektur:

... jeśli E1 ma podpisany typ i nieujemny va lue, a E1 ⨯ 2E2 można przedstawić w typie wyniku, to jest wynikową wartością; w przeciwnym razie zachowanie jest niezdefiniowane.

W rezultacie technika ta nie może być używana w wyrażeniu stałym, , która zepsuje znaczną ilość kodu.

Stwierdzając nacisk na rachunku działa poprawnie na większości (dwójki-dopełniacza) architektur. Jest więc zależne od podstawowej reprezentacji, na przykład dwójki-uzupełnienie.

1

W odniesieniu do lewej i prawej operatorzy przesunięcia, począwszy od C++ standardowej sekcji 5.8:

zachowanie jest nieokreślona, ​​gdy odpowiedni argument jest ujemny, lub większej niż lub równą długość w bitach promowanego lewej operand.

Potem mówi, że po lewej stronie operatora przesunięcia E1 < < E2 wyniki w niezdefiniowanej zachowań, gdy wszystkie poniższe warunki zostały spełnione:

  • Lewa operand ma podpisaną typu.
  • Albo lewy operand ma wartość ujemną lub ma wartość nieujemną, tak że E1 × 2^E2 nie jest reprezentowalny w wynikowym typie.

Również w przypadku operatora prawego przesunięcia E1 >> E2 zachowanie jest zależne od implementacji, jeśli lewy operand ma typ podpisu i wartość ujemną.

Bitowe operatory AND, XOR i OR są dobrze zdefiniowane dla wszystkich typów integralnych. Jest to określone odpowiednio w sekcjach 5.11, 5.12 i 5.13.

Należy jednak zauważyć, że reprezentacja podpisanych wartości integralnych może być uzupełnieniem dwójki, uzupełnieniem Ones lub wartością znaku. Większość kompilatorów używa jednak uzupełnienia Two. Należą do nich gcc, VC++, icl i Clang.

1

Operatorzy &, | i ^ są bitowe, i radzić sobie z poszczególnych bitów, więc będą robić dokładnie to, co napisane: zastosować mask.

Lewa zmiana operatora << jest nieco trudniejsza. Doprowadzi to do nieokreślonego zachowania, jeśli zmienisz wartość ujemną lub jeśli zmienisz 1, aby podpisać pozycję bitu lub dalej.

static_cast<integer>(1)<<static_cast<integer>(bits-1);

Wydaje się przesunąć 1 podpisać pozycji bitowej tam, i to jest niezdefiniowane zachowanie.