2016-06-21 17 views
5

Wykonałem głupi test na temat manipulacji bitami i znalazłem ten problem. Wykonuję ten kod:Lewe bity zmiany w c

int main(){ 
    unsigned int i; 
    for (i=1; i<34; i++) 
    { 
    unsigned long temp = i; 
    unsigned long mul = 1; 
    unsigned long val; 
    unsigned long one = 1; 

    // Way 1 
    while (temp--) 
     mul = mul << one; 

    // Way 2 
    val = (one<<i); 

    printf(" \n 1<<%i \n mul: 0x%X , val: 0x%X\n",i, mul, val); 
    } 
} 

Oczywiście wiem, że kiedy i> 31, zostanie wygenerowany przelew. Myślę, że obie części kodu (way1 i way2) powinny wyprowadzać ten sam wynik. Ale mam to (na końcu):

/* ... correct results from i=1 to i=31 ... */ 
1<<30 
mul: 0x40000000 , val: 0x40000000 

1<<31 
mul: 0x80000000 , val: 0x80000000 

1<<32 
mul: **0x0** , val: **0x1** 

1<<33 
mul: **0x0** , val: **0x2** 

Dlaczego, jeśli obie instrukcje są przesunięcia w lewo, program produkuje różne wyjścia? Wygląda na to, że część drogi2 tworzy okrągłą zmianę, ale nie wiem dlaczego, naprawdę uważam, że "mul" zawsze ma właściwą wartość.

skompilować pod maszyną Intel 32-bitową, wersja gcc 4.4.7

+5

Wynik operacji zmiany z wartością przesunięcia większą niż szerokość operandy jest niezdefiniowana według standardu C. –

+0

Po prostu opinię, ale powiedziałbym, że w tych przypadkach: 'mul << jeden' ​​i' jeden << i', lepiej jest po prostu użyć '1' niż nadać mu nazwę" one ". –

+0

To jest problem, tak. W tym przypadku najprawdopodobniej AND's prawy operand z 0x3F przed wykonaniem shft. –

Odpowiedz

2

W przypadku

val = (one<<i); 

gdy i dostaje większy lub równy 32, zachowanie jest niezdefiniowane.

 

Jednakże w przypadku

while (temp--) 
    mul = mul << one; 

do zmiany więcej niż 32, to przesuwają się zerem i wynik jest zdefiniowana (zero).

+0

Tak. Myślę, że masz rację. Z odpowiedzią Aif, myślę, że to jasne. Wielkie dzięki! – Alexi

9

Prawdopodobnie dlatego, że jest niezdefiniowane zachowanie? Według §6.5.7:

Jeżeli wartość prawego argumentu jest ujemna lub jest większa lub równa szerokości promowane lewego argumentu, zachowanie jest niezdefiniowane.

+0

Cóż, mogłem oczekiwać obu zachowań, ale tylko jedno z nich w obu przypadkach. Nie sądzisz, że obie operacje są równe? EDYCJA: Widzę odpowiedź deepmax i rozumiem cię teraz. Dzięki! – Alexi

+0

@Alexi Czy rozumiesz znaczenie czegoś nieokreślonego? –

+0

@EugeneSh. Tak, moim problemem było to, że rozumiałem znaczenie "niezdefiniowanego zachowania", ale nie wiedziałem, dlaczego istnieją dwa różne "nieokreślone zachowania", które odpowiadają (podobno) tej samej operacji. – Alexi

-3

Kiedy skompilować kod z -Wall I otrzymał skargi:

BASH> gcc -Wall left-shift.c 
left-shift.c: In function ‘main’: 
left-shift.c:21:12: warning: format ‘%X’ expects argument of type ‘unsigned int’, but argument 3 has type ‘long unsigned int’ [-Wformat=] 
    printf(" \n 1<<%i \n mul: 0x%X , val: 0x%X\n",i, mul, val); 
      ^
left-shift.c:21:12: warning: format ‘%X’ expects argument of type ‘unsigned int’, but argument 4 has type ‘long unsigned int’ [-Wformat=] 

Więc zmieniłem printf do

printf(" \n 1<<%i \n mul: 0x%lX , val: 0x%lX\n",i, mul, val); 

Dzięki tej zmianie "mul" i "val" przedstawia te same wyniki:

1<<30 
mul: 0x40000000 , val: 0x40000000 

1<<31 
mul: 0x80000000 , val: 0x80000000 

1<<32 
mul: 0x100000000 , val: 0x100000000 

1<<33 
mul: 0x200000000 , val: 0x200000000 

Informacje o systemie:

BASH> gcc --version 
gcc (Ubuntu 5.3.1-14ubuntu2.1) 5.3.1 20160413 
Copyright (C) 2015 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions. There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
BASH> uname -a 
Linux bm-pc-ubuntu 4.4.0-24-generiC#43-Ubuntu SMP Wed Jun 8 19:27:37 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux 
BASH> lsb_release --all 
No LSB modules are available. 
Distributor ID: Ubuntu 
Description: Ubuntu 16.04 LTS 
Release: 16.04 
Codename: xenial 
+0

Dzieje się tak dlatego, że twój komputer ma 64-bitową wersję, a jego 32-bitową. – s7amuser

+0

Zignorowanie tego ostrzeżenia, daje mi nawet strnager wyjście niż oryginalne: 1 << 30 mul: 0x40000000, Val: 0x40000000 1 << 31 mul: 0x80000000, val: 0x80000000 1 << 32 mul : 0x0, wartość: 0x0 1 << 33 mul: 0x0, wartość: 0x0 –

+0

Nie należy ignorować ostrzeżenia. Jeśli chcesz zobaczyć wyniki podobne do OP, spróbuj zwiększyć licznik pętli do 66. – s7amuser

0

Kiedy to zrobić:

val = (one<<i); 

Robisz jeden lewy shift przez i. Jeśli wartość i jest większa niż 31, wynikiem jest undefined behavior, co oznacza, że ​​wynik może być lub nie jest zgodny z oczekiwaniami.

Z sekcji 6.5.7.3 do C standard:

całkowite wyniki promowane są wykonywane na każdej z argumentów. Typ wyniku to wynik promowanego lewego operandu.Jeśli wartość prawego argumentu jest ujemna lub jest większa niż lub równa szerokości promowanego lewego operandu, zachowanie jest nieokreślone jako .

Jednakże, kiedy to zrobić:

while (temp--) 
    mul = mul << one; 

Robisz lewy przesunięcie o 1 i czasów. Jest to dobrze zdefiniowane, więc daje wartość, której oczekujesz.

Ponadto używasz %X do drukowania long, gdy powinieneś używać %lX. Powoduje to również niezdefiniowane zachowanie.