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.
1334
9
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
, mungkinpendek
danbyte
atauchar
). Dalam beberapa bahasa, menerapkan shift operator untuk setiap tipe data yang lebih kecil dariint
secara otomatis mengubah ukuran operan untuk menjadiint
.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:Pergeseran ini pola bit ke kiri satu posisi (
6 << 1
) akan menghasilkan angka 12: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 dengan6 * 2
, dan6 << 3
setara dengan6 * 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
):hasil di 3,221,225,472:
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:
ke kanan dengan salah satu posisi (
12 >>> 1
) akan kembali kami asli 6: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:
ke kiri 4 posisi (
939,524,102 << 4
), kita mendapatkan 2,147,483,744:dan kemudian bergeser kembali (
(939,524,102 << 4) >>> 4
) kita mendapatkan 134,217,734: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:
kami memiliki jumlah -2,147,483,552. Pergeseran ini ke kanan posisi 4 dengan aritmatika shift (-2,147,483,552 >> 4) akan memberikan:
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.
Let's mengatakan bahwa kita memiliki satu byte:
Menerapkan single kiri bitshift membuat kita:
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:
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:
Sekarang kita dapat mengkonversi ke kiri shift:
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:
Untuk mendapatkan nilai hijau anda akan melakukan hal ini:
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.
Masker yang tepat adalah 0x7E0 yang dalam biner adalah 0000011111100000 (yang merupakan 2016 dalam desimal).
Untuk menerapkan masker, anda menggunakan operator AND (&).
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:
Bit Masking & Pergeseran
Sedikit pergeseran ini sering digunakan pada tingkat rendah pemrograman grafis. Misalnya diberi warna pixel nilai dikodekan dalam 32-bit word.
Untuk pemahaman yang lebih baik, sama nilai biner diberi label dengan apa bagian mewakili warna apa bagian.
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:
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).Et voilá, kami memiliki integer yang mewakili jumlah yang hijau piksel warna:
Hal ini sering digunakan untuk encoding atau decoding format gambar seperti
jpg
,png
,...
.Satu gotcha adalah yang berikut adalah implementasi dependen (menurut standar ANSI):
x sekarang dapat 127 (01111111) atau masih -1 (11111111).
Dalam prakteknya,'s biasanya yang terakhir.
Saya menulis tips dan trik saja, mungkin menemukan berguna dalam tes/ujian.
n = n*2
:n = n<<1
n = n/2
:n = n>>1
!(n & (n-1))
n
:n |= (1 << x)
x&1 == 0
(bahkan)x ^ (1<<n)
Perhatikan bahwa dalam implementasi Java, jumlah bit untuk shift mod'd dengan ukuran sumber.
Misalnya:
sama dengan 2. Anda mungkin mengharapkan pergeseran bit ke kanan 65 kali akan nol semuanya, tapi itu's benar-benar setara dengan:
Hal ini berlaku untuk <<, >>, dan >>>. Saya belum mencobanya di dalam bahasa lain.
Beberapa Berguna Sedikit Operasi/Manipulasi dalam Python. Dilaksanakan @Ravi Prakash jawaban di python.
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.