2011-09-22 20 views
15

Na mojej kompilatora, następujący kod pseudo (wartości zastąpione binarny):Prawy shift i podpisane całkowitą

sint32 word = (10000000 00000000 00000000 00000000); 
word >>= 16; 

produkuje word z bitfield który wygląda tak:

(11111111 11111111 10000000 00000000) 

moje pytanie czy mogę polegać na tym zachowaniu na wszystkich platformach i kompilatorach C++?

Odpowiedz

23

z poniższego linku:
INT34-C. Do not shift an expression by a negative number of bits or by greater than or equal to the number of bits that exist in the operand

Code niezgodnych przykładu (przesunięcie w prawo)
Wynik E1 >> E2 jest E1 prawo przesunięty E2 pozycje bitów. Jeśli E1 ma niepodpisany typ lub jeśli E1 ma podpisany typ i wartość nieujemną, wartość wyniku jest integralną częścią ilorazu E1/2 E2. Jeśli E1 jest podpisany typ i wartość ujemną, w ten sposób wartość jest realizacja określone i mogą być arytmetyczna (ze znakiem) przechodzenia:
Arithmetic (signed) shift
lub logiczny (unsigned) przechodzenia:
Logical (unsigned) shift
ten niezgodnej przykład kodu nie sprawdza, czy prawy operand jest większy lub równy szerokości promowanego lewego argumentu, co pozwala na niezdefiniowane zachowanie.

unsigned int ui1; 
unsigned int ui2; 
unsigned int uresult; 

/* Initialize ui1 and ui2 */ 

uresult = ui1 >> ui2; 

dokonując założeń czy prawy shift jest zaimplementowany jako arytmetyki (podpisany) lub przesunięcie logiczne (unsigned) przesunięcie może również prowadzić do luk. Zobacz rekomendację INT13-C. Use bitwise operators only on unsigned operands.

+1

Czy nie było rekomendacji, która rzeczywiście skupiała się na tym problemie? Ponieważ na podstawie nazwy tej reguły nie ma tu zastosowania ... po prostu podajesz niektóre z podanych informacji podstawowych. –

12

Nie, nie możesz polegać na tym zachowaniu. Prawidłowe przesunięcie ujemnych wielkości (co do którego zakładam, że twój przykład dotyczy) jest zdefiniowane przez implementację.

+0

Dobra, to w porządku. Wciąż jednak zastanawiam się, czy kompilator tworzy plik binarny, który używa tej metody, czy działałby zgodnie z oczekiwaniami na większości urządzeń? –

+4

Jeśli skompilujesz coś dla pewnej architektury, to powinno działać tak samo we wszystkich implementacjach tej architektury. Na przykład, x86 ma różne operacje przesuwania dla przesunięć znaków i przesunięć bez znaku, a to kompilator decyduje, którego użyć. Prawdopodobnie nie zadziała w ogóle (przeczytaj: * cokolwiek w ogóle *, nie tylko to zachowanie) na innych architekturach. –

+0

To jest okropne ... – Claudiu

3

Liczby całkowite AFAIK mogą być reprezentowane jako wielkość znaku w języku C++, w którym to przypadku rozszerzenie znaku wypełniłoby się zerami. Więc nie możesz na tym polegać.

+0

Masz rację. Standard wprowadza pewne wymagania, które sprawiają, że dwa uzupełniają optymalną reprezentację, ale ogólnie liczby całkowite ze znakiem mogą być reprezentowane w dowolny sposób, jaki chce realizacja. –

6

W C++, nie. Jest to zależne od wdrożenia i/lub platformy.

W niektórych innych językach tak. Na przykład w języku Java operator >> jest precyzyjnie zdefiniowany tak, aby zawsze wypełniał za pomocą lewego najbardziej lewego znaku (tym samym zachowując znak). Operator >>> wypełnia używając 0s. Jeśli więc chcesz niezawodnego zachowania, jedną z możliwych opcji jest zmiana na inny język. (Chociaż oczywiście może to nie być opcja w zależności od okoliczności.)