Apa yang bitwise shift (bit-shift) operator dan bagaimana mereka bekerja?

I've telah mencoba untuk belajar C di waktu luang saya, dan bahasa-bahasa lain (C#, Java, dll.) memiliki konsep yang sama (dan sering sama operator) ...

Apa yang saya'm bertanya-tanya adalah, pada tingkat inti, yang tidak sedikit pergeseran (<<, >>, >>>) do, masalah apa yang bisa membantu memecahkan, dan apa yang gotchas mengintai di tikungan? Dengan kata lain, seorang pemula mutlak's panduan untuk sedikit pergeseran dalam segala kebaikan.

Mengomentari pertanyaan (4)
Larutan

Sedikit pergeseran operator melakukan persis apa namanya. Mereka menggeser bit. Berikut ini's singkat (atau tidak-jadi-singkat) pendahuluan untuk pergeseran yang berbeda operator.

Operator

  • >> adalah aritmatika (atau ditandatangani) shift kanan operator.
  • >>> adalah logis (atau unsigned) shift kanan operator.
  • << adalah shift kiri operator, dan memenuhi kebutuhan logis dan aritmatika shift.

Semua operator ini dapat diterapkan untuk nilai-nilai integer (int, lama, mungkin pendek dan byte atau char). Dalam beberapa bahasa, menerapkan shift operator untuk setiap tipe data yang lebih kecil dari int secara otomatis mengubah ukuran operan untuk menjadi int.

Perhatikan bahwa <<< tidak seorang operator, karena itu akan mubazir.

Juga perhatikan bahwa C dan C++ tidak membedakan antara kanan shift operator. Mereka hanya memberikan >> operator, dan hak-pergeseran perilaku adalah pelaksanaan yang ditetapkan untuk ditandatangani jenis. Sisa menjawab menggunakan C# / Java operator.

(Di semua mainstream C dan C++ implementasi termasuk gcc dan dentang/LLVM, >> pada ditandatangani jenis aritmatika. Beberapa kode mengasumsikan ini, tetapi isn't sesuatu yang standar jaminan. It's tidak tidak terdefinisi, meskipun; standar membutuhkan implementasi untuk menentukan satu atau lain cara. Namun, kiri bergeser dari negatif ditandatangani angka adalah perilaku tidak terdefinisi (signed integer overflow). Jadi kecuali jika anda perlu aritmatika shift kanan, it's biasanya ide yang baik untuk melakukan sedikit pergeseran dengan unsigned jenis.)


Shift kiri (<<)

Bilangan bulat yang disimpan dalam memori, sebagai rangkaian bit. Misalnya, nomor 6 disimpan sebagai 32-bit int akan sama:

00000000 00000000 00000000 00000110

Pergeseran ini pola bit ke kiri satu posisi (6 << 1) akan menghasilkan angka 12:

00000000 00000000 00000000 00001100

Seperti yang anda lihat, angka telah bergeser ke kiri dengan satu posisi, dan digit terakhir di sebelah kanan diisi dengan nol. Anda mungkin juga dicatat bahwa pergeseran kiri setara dengan perkalian dengan kekuatan 2. Jadi 6 << 1 setara dengan 6 * 2, dan 6 << 3 setara dengan 6 * 8. Baik mengoptimalkan compiler akan menggantikan perkalian dengan pergeseran bila mungkin.

Non-melingkar pergeseran

Harap dicatat bahwa ini adalah tidak melingkar shift. Pergeseran nilai ini ke kiri satu posisi (3,758,096,384 << 1):

11100000 00000000 00000000 00000000

hasil di 3,221,225,472:

11000000 00000000 00000000 00000000

Digit yang akan digeser "off the end" hilang. Tidak membungkus.


Logis shift kanan (>>>)

Logis shift kanan adalah sebaliknya ke kiri shift. Daripada bergerak bit ke kiri, mereka hanya bergerak ke kanan. Misalnya, pergeseran jumlah 12:

00000000 00000000 00000000 00001100

ke kanan dengan salah satu posisi (12 >>> 1) akan kembali kami asli 6:

00000000 00000000 00000000 00000110

Jadi kita melihat bahwa pergeseran ke kanan setara dengan pembagian kekuasaan 2.

Hilang bit yang hilang

Namun, pergeseran tidak dapat merebut kembali "hilang" bit. Misalnya, jika kita menggeser pola ini:

00111000 00000000 00000000 00000110

ke kiri 4 posisi (939,524,102 << 4), kita mendapatkan 2,147,483,744:

10000000 00000000 00000000 01100000

dan kemudian bergeser kembali ((939,524,102 << 4) >>> 4) kita mendapatkan 134,217,734:

00001000 00000000 00000000 00000110

Kita tidak bisa kembali kita nilai asli setelah kita telah kehilangan bit.


Aritmatika shift kanan (>>)

Aritmatika shift kanan adalah persis seperti yang logis shift kanan, kecuali bukan padding dengan nol, maka bantalan dengan most significant bit. Hal ini karena yang paling signifikan adalah masuk sedikit, atau sedikit yang membedakan bilangan positif dan negatif. Oleh padding dengan bit paling signifikan, aritmatika shift kanan adalah tanda-melestarikan.

Misalnya, jika kita menafsirkan ini pola bit sebagai angka negatif:

10000000 00000000 00000000 01100000

kami memiliki jumlah -2,147,483,552. Pergeseran ini ke kanan posisi 4 dengan aritmatika shift (-2,147,483,552 >> 4) akan memberikan:

11111000 00000000 00000000 00000110

atau jumlah -134,217,722.

Jadi kita melihat bahwa kita telah diawetkan tanda-tanda dari angka negatif dengan menggunakan aritmatika shift kanan, daripada logis shift kanan. Dan sekali lagi, kita melihat bahwa kita melakukan pembagian dengan kekuatan 2.

Komentar (22)

Let's mengatakan bahwa kita memiliki satu byte:

0110110

Menerapkan single kiri bitshift membuat kita:

1101100

Paling kiri nol bergeser keluar dari byte, dan baru nol ditambahkan ke ujung kanan dari byte.

Bit don't rollover; mereka dibuang. Itu berarti jika anda shift kiri 1101100 dan kemudian shift kanan itu, anda memenangkan't mendapatkan hasil yang sama kembali.

Pergeseran ke kiri oleh N setara dengan mengalikan dengan 2N.

Pergeseran kanan dengan N adalah (jika anda menggunakan [orang-orang' melengkapi][1]) adalah setara dengan membagi dengan 2N dan pembulatan ke nol.

Bitshifting dapat digunakan untuk gila-gilaan cepat perkalian dan pembagian, yang disediakan anda bekerja dengan power dari 2. Hampir semua grafis tingkat rendah rutinitas menggunakan bitshifting.

Misalnya, jalan kembali pada masa lalu, kita menggunakan mode 13h (320x200 256 warna) untuk permainan. Dalam Mode 13h, video memori diletakkan secara berurutan per pixel. Yang dimaksudkan untuk menghitung lokasi untuk pixel, anda akan menggunakan matematika berikut:

memoryOffset = (row * 320) + column

Sekarang, kembali di hari dan usia, kecepatan sangat penting, jadi kami akan menggunakan bitshifts untuk melakukan operasi ini.

Namun, 320 lebih dari dua, sehingga untuk mendapatkan sekitar ini kita harus mengetahui apa yang menjadi kekuatan dari dua yang ditambahkan bersama-sama membuat 320:

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

Sekarang kita dapat mengkonversi ke kiri shift:


(row * 320) = (row 
Komentar (6)

Bitwise operasi, termasuk sedikit pergeseran, adalah penting untuk low-level hardware atau tertanam pemrograman. Jika anda membaca spesifikasi untuk perangkat atau bahkan beberapa format file biner, anda akan melihat byte, kata, dan perkataan, dipecah menjadi non-byte blok bitfield, yang mengandung berbagai nilai-nilai kepentingan. Mengakses bit-bit tersebut-kolom untuk membaca/menulis adalah penggunaan yang paling umum.

Sederhana contoh nyata dalam pemrograman grafis adalah 16-bit pixel direpresentasikan sebagai berikut:

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

Untuk mendapatkan nilai hijau anda akan melakukan hal ini:

 #define GREEN_MASK  0x7E0
 #define GREEN_OFFSET  5

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

Penjelasan

Dalam rangka untuk mendapatkan nilai hijau-SATUNYA, yang dimulai pada offset 5 dan berakhir pada 10 (yaitu 6-bit), anda perlu menggunakan a (bit) masker, yang bila diterapkan terhadap seluruh 16-bit pixel, akan menghasilkan hanya bit kita tertarik.

#define GREEN_MASK  0x7E0

Masker yang tepat adalah 0x7E0 yang dalam biner adalah 0000011111100000 (yang merupakan 2016 dalam desimal).

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

Untuk menerapkan masker, anda menggunakan operator AND (&).

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

Setelah menerapkan masker, anda'll berakhir dengan 16-bit nomor yang benar-benar hanya 11-bit nomor sejak MSB di 11 bit. Hijau adalah benar-benar hanya 6-bit, jadi kita perlu skala ke bawah menggunakan shift kanan (11 - 6 = 5), maka penggunaan 5 sebagai offset (#define GREEN_OFFSET 5).

Juga umum adalah menggunakan sedikit pergeseran secara cepat perkalian dan pembagian dengan kekuatan 2:

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

Bit Masking & Pergeseran

Sedikit pergeseran ini sering digunakan pada tingkat rendah pemrograman grafis. Misalnya diberi warna pixel nilai dikodekan dalam 32-bit word.

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

Untuk pemahaman yang lebih baik, sama nilai biner diberi label dengan apa bagian mewakili warna apa bagian.

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

Let's katakan misalnya kita ingin mendapatkan nilai hijau ini piksel warna. Kita dapat dengan mudah mendapatkan bahwa nilai oleh masking dan pergeseran.

Masker kami:

                  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

Logis & operator memastikan bahwa hanya nilai-nilai di mana masker 1 disimpan. Hal terakhir yang kita sekarang harus lakukan, adalah untuk mendapatkan yang benar nilai integer dengan menggeser semua bit ke kanan dengan 16 tempat (logis shift kanan).

 green_value = masked_color >>> 16

Et voilá, kami memiliki integer yang mewakili jumlah yang hijau piksel warna:

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

Hal ini sering digunakan untuk encoding atau decoding format gambar seperti jpg,png,....

Komentar (1)

Satu gotcha adalah yang berikut adalah implementasi dependen (menurut standar ANSI):

char x = -1;
x >> 1;

x sekarang dapat 127 (01111111) atau masih -1 (11111111).

Dalam prakteknya,'s biasanya yang terakhir.

Komentar (3)

Saya menulis tips dan trik saja, mungkin menemukan berguna dalam tes/ujian.

  1. n = n*2: n = n<<1
  2. n = n/2: n = n>>1
  3. Memeriksa jika n adalah kekuatan dari 2 (1,2,4,8,...): check !(n & (n-1))
  4. Mendapatkan xth bit n: n |= (1 << x)
  5. Memeriksa jika x adalah genap atau ganjil: x&1 == 0 (bahkan)
  6. Beralih nth bit dari x: x ^ (1<<n)
Komentar (3)

Perhatikan bahwa dalam implementasi Java, jumlah bit untuk shift mod'd dengan ukuran sumber.

Misalnya:

(long) 4 >> 65

sama dengan 2. Anda mungkin mengharapkan pergeseran bit ke kanan 65 kali akan nol semuanya, tapi itu's benar-benar setara dengan:

(long) 4 >> (65 % 64)

Hal ini berlaku untuk <<, >>, dan >>>. Saya belum mencobanya di dalam bahasa lain.

Komentar (1)

Beberapa Berguna Sedikit Operasi/Manipulasi dalam Python. Dilaksanakan @Ravi Prakash jawaban di 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
Komentar (0)

Menyadari bahwa hanya 32 bit versi PHP yang tersedia di platform Windows.

Maka jika anda misalnya shift << atau >> lebih dari 31 bit, hasil unexpectable. Biasanya nomor asli bukan nol akan dikembalikan, dan itu bisa menjadi benar-benar rumit bug.

Tentu saja jika anda menggunakan versi 64 bit dari PHP (unix), anda harus menghindari pergeseran dengan lebih dari 63 bit. Namun, misalnya, MySQL menggunakan 64-bit BIGINT, jadi seharusnya tidak ada masalah kompatibilitas.

UPDATE: Dari PHP7 Windows, php membangun akhirnya mampu menggunakan full 64 bit bilangan bulat: Ukuran integer adalah platform-dependent, meskipun nilai maksimum sekitar dua miliar adalah nilai biasa (yang's 32 bit signed). 64-bit platform biasanya memiliki nilai maksimum sekitar 9E18, kecuali pada Windows sebelum PHP 7, di mana ia selalu 32 bit.

Komentar (0)