Bitsel işlem ve kullanım


102

Bu kodu düşünün:

x = 1        # 0001
x << 2       # Shift left 2 bits: 0100
# Result: 4

x | 2        # Bitwise OR: 0011
# Result: 3

x & 1        # Bitwise AND: 0001
# Result: 1

Python'daki (ve diğer dillerdeki) aritmetik operatörleri anlayabiliyorum, ancak 'bitsel' operatörleri hiçbir zaman tam olarak anlamadım. Yukarıdaki örnekte (bir Python kitabından), sola kaymayı anlıyorum ama diğer ikisini anlamıyorum.

Ayrıca, bitsel operatörler gerçekte ne için kullanılır? Bazı örnekleri takdir ediyorum.



Yanıtlar:


163

Bitsel operatörler, çok bitli değerler üzerinde çalışan, ancak kavramsal olarak her seferinde bir bit olan operatörlerdir.

  • ANDyalnızca her iki girişi de 1 ise 1'dir, aksi takdirde 0'dır.
  • OR1 durumunda olduğu , bir ya da her ikisi de , aksi durum 0, ve burada girişlerinden 1.
  • XOR1 yalnızca biri tam olarak onun girişlerinin aksi durum 0, 1 bulunmaktadır.
  • NOT yalnızca girişi 0 ise 1'dir, aksi takdirde 0'dır.

Bunlar genellikle en iyi doğruluk tabloları olarak gösterilebilir. Giriş olasılıkları üstte ve solda, sonuçtaki bit, girişlerin kesişme noktasında gösterilen dört değerden biridir (NOT durumunda sadece bir girişe sahip olduğu için iki) değer.

AND | 0 1     OR | 0 1     XOR | 0 1    NOT | 0 1
----+-----    ---+----     ----+----    ----+----
 0  | 0 0      0 | 0 1       0 | 0 1        | 1 0
 1  | 0 1      1 | 1 1       1 | 1 0

Bir örnek, bir tamsayının yalnızca daha düşük 4 bitini istiyorsanız, siz VE 15 ile (ikili 1111), yani:

    201: 1100 1001
AND  15: 0000 1111
------------------
 IS   9  0000 1001

Bu durumda 15'teki sıfır bit, sonuçtaki bitleri de sıfır olmaya zorlayarak etkili bir şekilde bir filtre görevi görür.

Ek olarak, >>ve <<genellikle bitsel operatörler olarak dahil edilirler ve bir değeri sırasıyla sağa ve sola belirli sayıda bit kadar "kaydırırlar", kaydırdığınız uçta dönen bitleri atarak ve sıfır bitleri beslerler. diğer ucu.

Yani mesela:

1001 0101 >> 2 gives 0010 0101
1111 1111 << 4 gives 1111 0000

Python'da sola kaymanın, bitlerin atıldığı sabit bir genişlik kullanmaması nedeniyle alışılmadık bir durum olduğuna dikkat edin - birçok dil veri türüne bağlı olarak sabit bir genişlik kullanırken, Python, fazladan bitleri karşılamak için genişliği basitçe genişletir. Python'da atma davranışını elde etmek için, and8 bitlik bir değerin dört bit sola kayması gibi, bitsel bir sola kaydırmayı takip edebilirsiniz :

bits8 = (bits8 << 4) & 255

Bunu göz önünde bulundurarak, bit operatörleri başka bir örnek size bir 8-bitlik birine paketi istediğiniz iki 4-bit değerlerine sahip ise, size operatörlerin üçünü kullanmak (edebilirsiniz olduğu left-shift, andve or):

packed_val = ((val1 & 15) << 4) | (val2 & 15)
  • İşlem & 15, her iki değerin de yalnızca daha düşük 4 bite sahip olmasını sağlayacaktır.
  • << 4Sola doğru taşımak için bir 4-bit kaymasıdır val1üst içine bir 8-bit değerinin bir 4 bit.
  • |Sadece bu iki buluşmanızı birleştirir.

Eğer val17 ve val24'tür:

                val1            val2
                ====            ====
 & 15 (and)   xxxx-0111       xxxx-0100  & 15
 << 4 (left)  0111-0000           |
                  |               |
                  +-------+-------+
                          |
| (or)                0111-0100

43

Tipik bir kullanım:

| belirli bir biti 1'e ayarlamak için kullanılır

& belirli bir biti test etmek veya temizlemek için kullanılır

  • Bir bit ayarlayın (burada n bit numarasıdır ve 0 en önemsiz bittir):

    unsigned char a |= (1 << n);

  • Biraz temizleyin:

    unsigned char b &= ~(1 << n);

  • Biraz geçiş yapın:

    unsigned char c ^= (1 << n);

  • Biraz test edin:

    unsigned char e = d & (1 << n);

Örneğin listenizin durumunu ele alalım:

x | 2grubu bit 1 için kullanılan x1

x & 10 bitinin x1 mi yoksa 0 mı olduğunu test etmek için kullanılır


38

Bitsel operatörler gerçekte ne için kullanılır? Bazı örnekleri takdir ediyorum.

Bitsel işlemlerin en yaygın kullanımlarından biri, onaltılık renkleri ayrıştırmaktır.

Örneğin, burada String gibi kabul eden ve Red, Green ve Blue değerlerinin bir demetini döndüren bir Python işlevi #FF09BE.

def hexToRgb(value):
    # Convert string to hexadecimal number (base 16)
    num = (int(value.lstrip("#"), 16))

    # Shift 16 bits to the right, and then binary AND to obtain 8 bits representing red
    r = ((num >> 16) & 0xFF)

    # Shift 8 bits to the right, and then binary AND to obtain 8 bits representing green
    g = ((num >> 8) & 0xFF)

    # Simply binary AND to obtain 8 bits representing blue
    b = (num & 0xFF)
    return (r, g, b)

Bunu başarmanın daha verimli yolları olduğunu biliyorum, ancak bunun hem kaydırmaları hem de bitsel boole işlemlerini gösteren gerçekten özlü bir örnek olduğuna inanıyorum.


14

Sanırım sorunun ikinci kısmı:

Ayrıca, bitsel operatörler gerçekte ne için kullanılır? Bazı örnekleri takdir ediyorum.

Yalnızca kısmen ele alınmıştır. Bunlar benim bu konudaki iki sentim.

Programlama dillerindeki bitsel işlemler, birçok uygulama ile uğraşırken temel bir rol oynar. Hemen hemen tüm düşük seviyeli hesaplama bu tür işlemler kullanılarak yapılmalıdır.

İki düğüm arasında veri göndermesi gereken tüm uygulamalarda, örneğin:

  • bilgisayar ağları;

  • telekomünikasyon uygulamaları (cep telefonları, uydu iletişimi vb.).

Alt düzey iletişim katmanında, veriler genellikle çerçeve adı verilen şekilde gönderilir . Çerçeveler, fiziksel bir kanal aracılığıyla gönderilen bayt dizileridir. Bu çerçeveler genellikle gerçek verileri ve başlık olarak adlandırılan şeyin parçası olan diğer bazı alanları (bayt olarak kodlanmış) içerir . Başlık genellikle iletişimin durumuyla ilgili bazı bilgileri (örneğin bayraklarla (bitlerle)), çerçeve sayaçlarıyla, düzeltme ve hata saptama kodlarıyla, vb. Kodlayan baytlar içerir. İletilen verileri bir çerçeve içinde almak ve veri göndermek için, bitsel işlemlerden emin olmanız gerekir.

Genel olarak, bu tür uygulamalarla uğraşırken, bir API mevcuttur, böylece tüm bu ayrıntılarla uğraşmak zorunda kalmazsınız. Örneğin, tüm modern programlama dilleri soket bağlantıları için kitaplıklar sağlar, bu nedenle aslında TCP / IP iletişim çerçevelerini oluşturmanıza gerek kalmaz. Ancak bu API'leri sizin için programlayan iyi insanları düşünün, onlar kesinlikle çerçeve yapısıyla uğraşmak zorundaydılar; düşük seviyeden daha yüksek seviyeli iletişime gidip gelmek için her türlü bitsel işlemi kullanmak.

Somut bir örnek olarak, birisinin size doğrudan telekomünikasyon donanımı tarafından yakalanan ham verileri içeren bir dosya verdiğini hayal edin. Bu durumda, kareleri bulmak için, dosyadaki ham baytları okumanız ve verileri parça parça tarayarak bir tür senkronizasyon kelimesi bulmaya çalışmanız gerekir. Senkronizasyon kelimelerini tanımladıktan sonra, aktarılan gerçek verileri almak için gerçek çerçeveleri almanız ve gerekirse onları SHIFT (ve bu hikayenin sadece başlangıcıdır) gerekir.

Bir başka çok farklı düşük seviyeli uygulama ailesi, paralel ve seri bağlantı noktaları gibi bazı (bir tür eski) bağlantı noktalarını kullanarak donanımı kontrol etmeniz gerektiğinde ortaya çıkar. Bu bağlantı noktaları, bazı baytlar ayarlanarak kontrol edilir ve bu baytların her bitinin, talimatlar açısından o bağlantı noktası için belirli bir anlamı vardır (örneğin bkz . Http://en.wikipedia.org/wiki/Parallel_port ). Bu donanımla bir şeyler yapan bir yazılım oluşturmak istiyorsanız, yürütmek istediğiniz talimatları bağlantı noktasının anlayacağı baytlara çevirmek için bitsel işlemlere ihtiyacınız olacaktır.

Örneğin, başka bir aygıtı kontrol etmek için paralel bağlantı noktasına bağlı bazı fiziksel düğmeleriniz varsa, bu yazılım uygulamasında bulabileceğiniz bir kod satırıdır:

read = ((read ^ 0x80) >> 4) & 0x0f; 

Umarım bu katkıda bulunur.


Ben eklemek istiyorum en.wikipedia.org/wiki/Bit_banging paralel ve bit seviyesinde işlemler yararlı olabilir örnek olarak seri portlar hakkında okuma özellikle keşfetmek için başka yol olarak.
Dan

6

Umarım bu, bu ikisini açıklığa kavuşturur:

x | 2

0001 //x
0010 //2

0011 //result = 3

x & 1

0001 //x
0001 //1

0001 //result = 1

4
Oops ... batının en hızlı silahı olmaya çalıştı .... sonunda iki kişilik ikiliyi bile bilmeyen bir aptal oldu :( Düzeltildi.
Amarghosh

1
x & 1etkiyi olduğu kadar iyi göstermez x & 2.
dansalmo

5

0'ı yanlış ve 1'i doğru olarak düşünün. Daha sonra bitsel ve (&) ve veya (|) normal gibi çalışır ve veya dışında değerdeki tüm bitleri aynı anda yaparlar. Tipik olarak, ayarlanabilen 30 seçeneğiniz varsa (örneğin bir pencerede çizim stilleri olarak), her birini ayarlamak veya kaldırmak için 30 ayrı boole değeri iletmek zorunda kalmak istemiyorsanız, bunların bayraklar için kullanıldığını görürsünüz, böylece | Seçenekleri tek bir değerde birleştirmek için ve ardından her seçeneğin ayarlanıp ayarlanmadığını kontrol etmek için & tuşunu kullanın. Bu tip bayrak geçişi OpenGL tarafından yoğun bir şekilde kullanılmaktadır. Her bit ayrı bir bayrak olduğundan, ikisinin üsleri üzerinde bayrak değerleri alırsınız (diğer bir deyişle, yalnızca bir bit kümesine sahip sayılar) 1 (2 ^ 0) 2 (2 ^ 1) 4 (2 ^ 2) 8 (2 ^ 3) ikinin gücü, bayrak açıksa hangi bitin ayarlandığını söyler.

Ayrıca 2 = 10'a dikkat edin, yani x | 2, 110 (6) değil, 111 (7) Bitlerin hiçbiri üst üste gelmiyorsa (bu durumda bu doğrudur) | toplama gibi davranır.


5

Yukarıda bahsettiğini görmedim ama bazı kişilerin aritmetik işlemler için sola ve sağa kaydırmayı kullandığını da göreceksiniz. X ile sola kaydırma, 2 ^ x ile çarpmaya eşdeğerdir (taşmadığı sürece) ve sağa kaydırma, 2 ^ x'e bölmeye eşdeğerdir.

Son zamanlarda x << 1 ve x >> 1'i ikiye katlamak ve yarıya indirmek için kullanan insanlar gördüm, ancak onların sadece akıllı olmaya mı çalıştıklarından veya normal operatörlere göre gerçekten belirgin bir avantaj olup olmadığından emin değilim.


1
Python hakkında bilgim yok, ancak C veya hatta daha düşük montaj gibi daha düşük seviyeli dillerde, bitsel kaydırma çok daha verimli. Farkı görmek için, bunu her şekilde yaparak C'ye bir program yazabilir ve sadece assembly koduna göre derleyebilirsiniz (ya da assembly dilini biliyorsanız, bunu zaten biliyorsunuzdur :)). Talimat sayısındaki farkı görün.
0xc0de

2
Bit kaydırma operatörlerini kullanmaya karşı olan argümanım, çoğu modern derleyicinin muhtemelen aritmetik işlemleri zaten optimize ettiği ve böylece zekanın en iyi ihtimalle tartışmalı veya en kötü ihtimalle derleyiciyle savaştığıdır. C, derleyiciler veya CPU tasarımları konusunda uzmanlığım yok ve bu yüzden haklı olduğumu varsaymıyorum. :)
P. Stallworth

Bu daha yüksek olmalı. Bitsel operatörü tam olarak bu şekilde kullanan bazı kodlarla uğraşmak zorunda kaldım ve bu cevap işleri anlamama yardımcı oldu.
Philippe Oger

4

Setleri

Kümeler matematiksel işlemler kullanılarak birleştirilebilir.

  • Birleştirme operatörü |, her ikisinde de öğeleri içeren yeni bir set oluşturmak için iki seti birleştirir.
  • Kesişim operatörü &, öğeleri yalnızca her ikisinde de alır.
  • Fark operatörü -, ilk kümedeki öğeleri alır, ancak ikinci kümede almaz.
  • Simetrik fark operatörü ^, her iki kümedeki öğeleri alır, ancak ikisini birden almaz.

Kendin dene:

first = {1, 2, 3, 4, 5, 6}
second = {4, 5, 6, 7, 8, 9}

print(first | second)

print(first & second)

print(first - second)

print(second - first)

print(first ^ second)

Sonuç:

{1, 2, 3, 4, 5, 6, 7, 8, 9}

{4, 5, 6}

{1, 2, 3}

{8, 9, 7}

{1, 2, 3, 7, 8, 9}

Bu cevap soruyla tamamen alakasız ve başka bir yerden kopyalanıp yapıştırılmış gibi görünüyor.
doctaphred

Soru "Bitsel operatörler gerçekte ne için kullanılır?" Diye sorar. Bu cevap, bitsel operatörlerin daha az bilinen, ancak çok faydalı bir kullanımını sağlar.
Taegyung

3

Bu örnek size dört 2 bitlik değerin tümü için işlemleri gösterecektir:

10 | 12

1010 #decimal 10
1100 #decimal 12

1110 #result = 14

10 & 12

1010 #decimal 10
1100 #decimal 12

1000 #result = 8

İşte bir kullanım örneği:

x = raw_input('Enter a number:')
print 'x is %s.' % ('even', 'odd')[x&1]

2

Başka bir yaygın kullanım durumu, dosya izinlerini değiştirmek / test etmektir. Python stat modülüne bakın: http://docs.python.org/library/stat.html .

Örneğin, bir dosyanın izinlerini istenen bir izin setiyle karşılaştırmak için aşağıdaki gibi bir şey yapabilirsiniz:

import os
import stat

#Get the actual mode of a file
mode = os.stat('file.txt').st_mode

#File should be a regular file, readable and writable by its owner
#Each permission value has a single 'on' bit.  Use bitwise or to combine 
#them.
desired_mode = stat.S_IFREG|stat.S_IRUSR|stat.S_IWUSR

#check for exact match:
mode == desired_mode
#check for at least one bit matching:
bool(mode & desired_mode)
#check for at least one bit 'on' in one, and not in the other:
bool(mode ^ desired_mode)
#check that all bits from desired_mode are set in mode, but I don't care about 
# other bits.
not bool((mode^desired_mode)&desired_mode)

Sonuçları boole olarak belirledim, çünkü sadece doğruyu veya yalanı önemsiyorum, ancak her biri için bin () değerlerini yazdırmak faydalı olacaktır.


1
Son örnekte yanılıyorsun. Burada nasıl görünüyor gibi olmalıdır: not bool((mode ^ desired_mode) & 0777). Veya (daha kolay anlamak için): not (mode & 0777) ^ desired_mode == 0. VE sadece ilginç bitler bırakacak, XOR istenen tüm bitlerin ne ayarlandığını kontrol edecek. Açık == 0karşılaştırma daha anlamlı bool().
Vadim Fint

Bunun dosya işlemlerine özgü olduğunu sanmıyorum. Örneğin, PyQt için benzer bir şey yaparsınız setWindowFlags. Örnek: setWindowFlags(SplashScreen | WindowStaysOnTopHint). Bunu hala kafa karıştırıcı buluyorum, çünkü 'açık' olarak ayarladığınız bir geçiş gibi görünüyor, bu nedenle böyle bir durumda 've' için daha sezgisel görünüyor.
eric

2

Tamsayıların bit temsilleri genellikle bilimsel hesaplamada doğru-yanlış bilgi dizilerini temsil etmek için kullanılır, çünkü bitsel bir işlem, bir boole dizisi boyunca yinelemekten çok daha hızlıdır. (Daha yüksek seviyeli diller, bit dizisi fikrini kullanabilir.)

Bunun güzel ve oldukça basit bir örneği, Nim oyununun genel çözümüdür. Wikipedia sayfasındaki Python koduna bir göz atın . Bitsel özel veya, yoğun şekilde kullanılır .^


1

Bir dizi elemanının iki değer arasında nerede olduğunu bulmanın daha iyi bir yolu olabilir, ancak bu örnekte gösterildiği gibi, & burada çalışır, oysa ve çalışmaz.

import numpy as np
a=np.array([1.2, 2.3, 3.4])
np.where((a>2) and (a<3))      
#Result: Value Error
np.where((a>2) & (a<3))
#Result: (array([1]),)

1

Bahsedildiğini görmedim, Bu örnek size 2 bit değer için (-) ondalık işlemi gösterecek: AB (yalnızca A, B içeriyorsa)

bu işlem, programımızda bitleri temsil eden bir fiil tuttuğumuzda gereklidir. bazen bitler eklememiz gerekir (yukarıdaki gibi) ve bazen bitleri kaldırmamız gerekir (eğer fiil içeriyorsa)

111 #decimal 7
-
100 #decimal 4
--------------
011 #decimal 3

python ile: 7 & ~ 4 = 3 (7'den 4'ü temsil eden bitleri çıkarın)

001 #decimal 1
-
100 #decimal 4
--------------
001 #decimal 1

python ile: 1 & ~ 4 = 1 (1'den 4'ü temsil eden bitleri çıkarın - bu durumda 1, 'içerir' 4 değildir) ..


0

Bir tamsayının bitlerini işlemek yararlı olsa da, genellikle bitlere kadar belirtilebilen ağ protokolleri için, daha uzun bayt dizilerinin (kolayca bir tam sayıya dönüştürülemeyen) işlenmesi gerekebilir. Durumda kullanmak faydalıdır Bit dizisi verileri üzerinde ikilik işlemleri için izin veren kütüphane - örneğin kimse (ya da diğer bitsel işlemleri gerçekleştirmek) bir dize olarak veya altıgen ve bit vardiya olarak dize 'abcdefghijklmnopq' alabilirsiniz:

>>> import bitstring
>>> bitstring.BitArray(bytes='ABCDEFGHIJKLMNOPQ') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')
>>> bitstring.BitArray(hex='0x4142434445464748494a4b4c4d4e4f5051') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')

0

aşağıdaki bitsel operatörler: & , | , ^ ve ~ dönüş değerleri (girişlerine göre) aynı mantık kapıları sinyalleri etkiler. Devreleri taklit etmek için onları kullanabilirsiniz.


0

Bitleri çevirmek için (yani, 1'i tamamlayıcı / ters çevirmek) aşağıdakileri yapabilirsiniz:

Tüm 1'lerle ExORed değeri ters çevirme ile sonuçlandığından, belirli bir bit genişliği için onları ters çevirmek için ExOR'u kullanabilirsiniz.

In Binary
a=1010 --> this is 0xA or decimal 10
then 
c = 1111 ^ a = 0101 --> this is 0xF or decimal 15
-----------------
In Python
a=10
b=15
c = a ^ b --> 0101
print(bin(c)) # gives '0b101'
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.