Ce schimbare la nivel de bit (bit-shift) operatorii și cum funcționează ele?

Am'am fost încercarea de a afla C în timpul meu liber, și alte limbaje (C#, Java, etc.) au același concept (și de multe ori aceiași operatori) ...

Ceea ce am'm întrebam, la un nivel de bază, ceea ce face pic-shifting (<<, >>, >>>) fac, ce probleme poate să-l ajute rezolva, si ce chestii trage cu urechea în jurul valorii de cot? Cu alte cuvinte, un incepator absolut's ghid de biți deplasare în toată bunătatea.

Comentarii la întrebare (4)
Soluția

Bit deplasare operatorii de a face exact ceea ce sugerează și numele lor. Ei trecerea de biți. Aici's un scurt (sau nu-așa-scurtă) introducere la diferite tura de operatori.

Operatorii

  • >> este media aritmetică (sau semnat) shift din dreapta operatorului.
  • `>>> este logic (sau nesemnate) shift din dreapta operatorului.
  • << este o schimbare la stânga operatorului, și satisface nevoile de atât de logică și aritmetică schimburi.

Toți acești operatori pot fi aplicate la valori întregi (int, timp, eventual scurt și octet sau char). În unele limbi, aplicarea schimbare operatorii la orice tip de date mai mici decât int redimensionează automat operand să fie un int.

Rețineți că <<< nu este un operator, pentru că ar fi redundant.

De asemenea, rețineți că C și C++ nu fac distincție între dreptul de schimbare operatorii. Acestea oferă doar >> operator, și chiar schimbarea de comportament este punerea în aplicare definite pentru semnat tipuri. Restul de răspunsul utilizează C# / Java operatori.

(În toate de masă C și C++ implementări inclusiv ccg și zăngănit/LLVM, `>> pe a semnat tipuri este aritmetică. Un cod nu își asumă acest lucru, dar e't ceva standard garanții. L's nu nedefinit, deși; standard necesită implementări de a defini într-un fel sau altul. Cu toate acestea, stânga schimburi de negative semnat numere e comportament nedefinit (semnat integer overflow). Deci, dacă aveți nevoie de aritmetică la dreapta shift, l's, de obicei, o idee bună de a face ceva-deplasarea cu nesemnate tipuri.)


Shift stânga (<<)

Numerele întregi sunt stocate în memorie, ca o serie de biți. De exemplu, numărul 6 stocate ca un 32-bit int ar fi:

00000000 00000000 00000000 00000110

Schimbarea acestui model de biți la stânga cu o poziție (6 << 1) ar rezulta în număr de 12:

00000000 00000000 00000000 00001100

După cum puteți vedea, cifrele au deplasat la stânga cu o poziție, și ultima cifră din dreapta este umplut cu un zero. S-ar putea, de asemenea, rețineți că schimbarea stânga este echivalentă cu înmulțirea cu puteri ale lui 2. Deci 6 << 1 este echivalent cu 6 * 2", și " 6 << 3 este echivalent cu 6 * 8. O buna optimizare compilatorul va înlocui înmulțiri cu schimburi atunci când este posibil.

Non-circulară trecerea

Vă rugăm să rețineți că acestea sunt nu ture circulare. Schimbarea acestei valori la stânga cu o poziție (3,758,096,384 << 1):

11100000 00000000 00000000 00000000

rezultate în 3,221,225,472:

11000000 00000000 00000000 00000000

Cifra care devine mutat "off the end" este pierdut. Nu înfășurați în jurul valorii de.


Logic dreapta shift (>>>)

Logic shift din dreapta este invers la stânga shift. Mai degrabă decât se deplasează biți la stânga, ei pur și simplu muta la dreapta. De exemplu, deplasarea în număr de 12:

00000000 00000000 00000000 00001100

la dreapta cu o poziție (12 >>> 1) va primi înapoi noastre originale 6:

00000000 00000000 00000000 00000110

Deci, vedem că deplasarea la dreapta este echivalentă cu divizia de puteri ale lui 2.

Pierdut biți sunt plecat

Cu toate acestea, o schimbare nu poate revendica "pierdut" de biți. De exemplu, dacă ne-am schimba acest model:

00111000 00000000 00000000 00000110

la stânga 4 pozitii (939,524,102 << 4), vom obține 2,147,483,744:

10000000 00000000 00000000 01100000

și apoi trecerea înapoi ((939,524,102 << 4) >>> 4) vom obține 134,217,734:

00001000 00000000 00000000 00000110

Nu putem primi înapoi valoarea inițială după ce ne-am pierdut de biți.


Aritmetică dreapta shift (>>)

Aritmetica dreapta shift este exact ca logic shift din dreapta, cu excepția în loc de umplutură cu zero, tampoane cu cel mai semnificativ bit. Acest lucru este pentru că cel mai semnificativ bit este semnul ** bit sau bit care distinge numerele pozitive și negative. De umplutură cu cel mai semnificativ bit, aritmetica dreapta shift este semn de conservare.

De exemplu, dacă am interpreta acest model de biți ca un număr negativ:

10000000 00000000 00000000 01100000

avem numărul -2,147,483,552. Trecerea aceasta spre dreapta 4 poziții cu aritmetica shift (-2,147,483,552 >> 4) ne va da:

11111000 00000000 00000000 00000110

sau numărul -134,217,722.

Deci, vedem că ne-am păstrat semn de numere negative prin utilizarea aritmetică shift din dreapta, mai degrabă decât logică shift dreapta. Și din nou, vom vedea că suntem efectuarea divizia de puteri ale lui 2.

Comentarii (22)

Las's spune ca avem un singur octet:

0110110

Aplicarea unei singure stânga bitshift ne devine:

1101100

Cea mai din stânga zero a fost mutat din octet, și un nou zero a fost anexată la capătul din dreapta al octet.

Biți don't rollover; ele sunt aruncate. Asta înseamnă că dacă ai plecat schimbare 1101100 și apoi la dreapta shift, ai castigat't obține același rezultat înapoi.

Deplasarea la stânga cu N este echivalentă cu înmulțirea cu 2N.

Deplasarea la dreapta cu N este (dacă utilizați [cele' complement][1]) este echivalentă cu împărțirea la 2N și rotunjirea la zero.

Bitshifting poate fi folosit pentru un incredibil de rapid, înmulțire și împărțire, cu condiția să lucrați cu o putere de 2. Aproape toate low-level rutine grafice utilizarea bitshifting.

De exemplu, drumul înapoi în zilele de demult, am folosit modul 13h (320x200 cu 256 culori) pentru jocuri. În Modul 13h, memoria video a fost pus secvențial per pixel. Asta a însemnat pentru a calcula locația pentru un pixel, ar trebui să utilizați următoarele matematica:

memoryOffset = (row * 320) + column

Acum, înapoi în această zi și de vârstă, viteza a fost critică, așa că ne-ar folosi bitshifts de a face această operațiune.

Cu toate acestea, 320 nu este o putere a lui doi, astfel încât pentru a obține în jurul valorii de acest lucru trebuie să aflăm ce este o putere a lui doi care adună face 320:

(row * 320) = (row * 256) + (row * 64)

Acum avem posibilitatea de a converti în stânga schimburi:


(row * 320) = (row 
Comentarii (6)

Operații la nivel de bit, inclusiv pic de schimbare, sunt fundamentale pentru nivel scăzut de hardware sau de programare. Dacă ai citit o specificație pentru un dispozitiv sau chiar unele binare formate de fișiere, veți vedea octeți, cuvinte și cuvinte, împărțite în non-byte aliniat bitfields, care conține diferite valori de interes. Accesarea acestor pic-câmpuri de citire/scriere este cea mai comună utilizare.

Un simplu exemplu real în grafica de programare este că un 16-bit pixel este reprezentat după cum urmează:

  bit | 15| 14| 13| 12| 11| 10| 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1  | 0 |
      |       Blue        |         Green         |       Red          |

Pentru a ajunge la verde valoarea pe care ar face acest lucru:

 #define GREEN_MASK  0x7E0
 #define GREEN_OFFSET  5

 // Read green
 uint16_t green = (pixel & GREEN_MASK) >> GREEN_OFFSET;

Explicație

În scopul de a obține valoarea de verde, care începe de la offset 5 și se termină la 10 (respectiv 6-biți), trebuie să utilizați un (bit) masca, care atunci când este aplicat împotriva întregii 16-bit pixel, va ceda doar de biți suntem interesați.

#define GREEN_MASK  0x7E0

Masca corespunzătoare este 0x7E0 care în binar este 0000011111100000 (care este 2016 în zecimal).

uint16_t green = (pixel & GREEN_MASK) ...;

Pentru a aplica o masca, puteți folosi ȘI operatorul (&).

uint16_t green = (pixel & GREEN_MASK) >> GREEN_OFFSET;

După aplicarea masca,'ll end up cu un număr de 16 de biți, care este de fapt doar o 11-numărul de biți încă de la MSB este în cea de-a 11 biți. Verde este, de fapt, doar 6-biți, deci, avem nevoie pentru a scala în jos, folosind o dreapta shift (11 - 6 = 5), prin urmare, utilizarea de 5 ca offset (#define GREEN_OFFSET 5).

De asemenea, comuna este folosind pic schimburi de repede înmulțire și împărțire cu puteri ale lui 2:

 i = y;  // i /= 2^y;
Comentarii (1)

Bit de Mascare & Trecerea

Cam schimbarea este adesea folosit în nivelul scăzut de programare grafică. De exemplu, un anumit pixel valoare de culoare codificate într-un 32-bit cuvânt.

 Pixel-Color Value in Hex:    B9B9B900
 Pixel-Color Value in Binary: 10111001  10111001  10111001  00000000

Pentru o mai bună înțelegere, aceeași valoare binară etichetate cu ce secțiuni reprezintă ce culoare parte.

                                 Red     Green     Blue       Alpha
 Pixel-Color Value in Binary: 10111001  10111001  10111001  00000000

Las's spune, de exemplu, vrem să ajungem verde valoarea acestui pixeli de culoare. Putem obține cu ușurință că valoarea de mascare și deplasare.

Masca noastre:

                  Red      Green      Blue      Alpha
 color :        10111001  10111001  10111001  00000000
 green_mask  :  00000000  11111111  00000000  00000000

 masked_color = color & green_mask

 masked_color:  00000000  10111001  00000000  00000000

Logic & operatorul se asigură că numai valorile în cazul în care masca este 1 sunt păstrate. Ultimul lucru de care avem acum de-a face, este de a ajunge la valoare întreagă prin schimbarea tuturor acestor biți la dreapta de 16 locuri (logical shift dreapta).

 green_value = masked_color >>> 16

Et voilá, avem întreg ce reprezintă cantitatea de verde în pixeli de culoare:

 Pixels-Green Value in Hex:     000000B9
 Pixels-Green Value in Binary:  00000000 00000000 00000000 10111001 
 Pixels-Green Value in Decimal: 185

Acest lucru este adesea folosit pentru codare sau de decodare formate de imagine ca jpg,png,...`.

Comentarii (1)

Unul te-am prins este că următoarele este punerea în aplicare dependente (în conformitate cu standardul ANSI):

char x = -1;
x >> 1;

x poate fi acum 127 (01111111) sau încă -1 (11111111).

În practică, se's, de obicei acesta din urmă.

Comentarii (3)

Am scris sfaturi și trucuri numai, ar putea găsi utile în teste/examene.

  1. n = n*2: n = n<<1
  2. n = n/2: n = n>>1
  3. Verificarea dacă n este putere a lui 2 (1,2,4,8,...): check!(n & (n-1))`
  4. Obtinerea xlea pic de "n": n |= (1 << x)
  5. Verificarea dacă x este par sau impar: x&1 == 0 (chiar)
  6. Comuta nlea cam x: x ^ (1<<n)`
Comentarii (3)

Rețineți că, în implementarea Java, numărul de biți pentru a schimba este în mod'd de dimensiunea sursei.

De exemplu:

(long) 4 >> 65

este egal cu 2. S-ar putea aștepta trecerea de biți pentru dreptul de 65 de ori ar fi zero totul, dar's, de fapt, echivalentul a:

(long) 4 >> (65 % 64)

Acest lucru este valabil pentru <<, >> și >>>. Nu am încercat-o și în alte limbi.

Comentarii (1)

Unele Utile Pic Operarea/Manipularea în Python. Implementat @Ravi Prakash răspunsuri în python.


# basic bit operations
# int to bin
print(bin(10))

# bin to int
print(int('1010',2))

# multiplying x with 2 .... x**2== x >1)

# modulo x with 2 .... x%2 == x&1
if 20&1==0:
    print("20 is a even number")

# check if n is power of 2 : check !(n & (n-1))
print(not(33 &(33-1)))

# getting xth bit of n : (n>>x)&1
print((10>>2)&1) # bin of 10==1010 and 2nd bit is 0

# toggle nth bit of x : x^(1
Comentarii (0)

Fi conștienți de faptul că doar 32 de biți versiune de PHP este disponibil pe platforma Windows.

Apoi, dacă ai, de exemplu, shift << sau >> mai mult de 31 de biți, rezultatele sunt unexpectable. De obicei, numărul original, în loc de zerouri vor fi returnate, iar acesta poate fi un foarte dificil bug.

Desigur, dacă utilizați versiunea pe 64 de biți de PHP (unix), ar trebui să evite trecerea prin mai mult de 63 de biți. Cu toate acestea, de exemplu, MySQL folosește 64-bit BIGINT, așa că nu ar trebui să existe probleme de compatibilitate.

UPDATE: Din PHP7 Windows, php construiește sunt în cele din urmă capabil de a utiliza complet pe 64 de biți numere întregi: Dimensiunea de un număr întreg este dependente de platformă, deși o valoare maximă de aproximativ două miliarde de euro este de obicei o valoare (ca's de 32 de biți semnat). Platforme de 64-bit, de obicei, au o valoare maximă de aproximativ 9E18, cu excepția Windows înainte de PHP 7, unde a fost mereu pe 32 de biți.

Comentarii (0)