web-dev-qa-db-fra.com

Opération et utilisation au niveau des bits

Considérons ce code:

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

Je peux comprendre les opérateurs arithmétiques en Python (et d’autres langages), mais je n’ai jamais très bien compris les opérateurs «au niveau du bit». Dans l'exemple ci-dessus (tiré d'un livre Python), je comprends le décalage à gauche mais pas les deux autres.

Aussi, à quoi servent réellement les opérateurs de bits? J'apprécierais quelques exemples.

85
3zzy

Les opérateurs au niveau du bit sont des opérateurs qui travaillent sur des valeurs multi-bits, mais conceptuellement bit par bit.

  • AND est 1 seulement si les deux de ses entrées sont 1, sinon c'est 0.
  • OR est 1 si une ou les deux de ses entrées sont 1, sinon c'est 0.
  • XOR est 1 seulement si exactement une de ses entrées sont 1, sinon c'est 0.
  • NOT n'est égal à 1 que si son entrée est 0, sinon c'est 0.

Celles-ci peuvent souvent être mieux présentées sous forme de tables de vérité. Les possibilités d’entrée sont en haut et à gauche, le bit résultant est l’une des quatre valeurs (deux dans le cas de NOT car il n’a qu’une entrée) affichées à l’intersection des entrées.

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

Un exemple est si vous voulez seulement les 4 bits les plus bas d'un entier, vous l'utilisez avec 15 (binaire 1111), ainsi:

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

Les bits nuls sur 15 dans ce cas agissent effectivement comme un filtre, forçant les bits du résultat à être également nuls.

De plus, >> et << sont souvent inclus en tant qu'opérateurs au niveau du bit, et ils "décalent" une valeur respectivement vers la droite et vers la gauche d'un certain nombre de bits, en jetant les bits de la fin vers laquelle vous vous déplacez et en alimentant les bits zéro à l'autre extrémité.

Donc, par exemple:

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

Notez que le décalage à gauche dans Python est inhabituel dans la mesure où il n'utilise pas une largeur fixe où les bits sont ignorés - alors que de nombreuses langues utilisent une largeur fixe basée sur le type de données, Python étend simplement la largeur pour prendre en charge des bits supplémentaires. Pour obtenir le comportement de suppression dans Python, vous pouvez suivre un décalage à gauche avec une variable and au niveau du bit, comme dans une valeur de 8 bits décalant les quatre bits de gauche:

bits8 = (bits8 << 4) & 255

En gardant cela à l'esprit, un autre exemple d'opérateur au niveau du bit est que si vous souhaitez insérer deux valeurs 4 bits dans une valeur 8 bits, vous pouvez utiliser les trois opérateurs (left-shift, and et or):

packed_val = ((val1 & 15) << 4) | (val2 & 15)
  • L'opération & 15 s'assurera que les deux valeurs ont uniquement les 4 bits les plus bas.
  • Le << 4 est un décalage de 4 bits permettant de déplacer val1 dans les 4 premiers bits d'une valeur de 8 bits.
  • Le | combine simplement ces deux éléments ensemble.

Si val1 est 7 et val2 est 4:

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

Une utilisation typique:

| permet de définir un bit sur 1

& est utilisé pour tester ou effacer un certain bit

  • Définissez un bit (où n est le numéro du bit et 0 le bit le moins significatif):

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

  • Effacer un peu:

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

  • Basculer un peu:

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

  • Testez un peu:

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

Prenons le cas de votre liste par exemple:

x | 2 est utilisé pour définir le bit 1 de x sur 1

x & 1 est utilisé pour tester si le bit 0 de x vaut 1 ou 0

41
pierrotlefou

à quoi servent réellement les opérateurs de bits? J'apprécierais quelques exemples.

L’une des utilisations les plus courantes des opérations au niveau des bits est l’analyse des couleurs hexadécimales.

Par exemple, voici une fonction Python qui accepte une chaîne telle que #FF09BE et renvoie un tuple de ses valeurs rouge, verte et bleue.

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)

Je sais qu'il existe des moyens plus efficaces pour y parvenir, mais je pense qu'il s'agit d'un exemple très concis illustrant à la fois les changements et les opérations booléennes au niveau des bits.

34
bguiz

Je pense que la deuxième partie de la question: 

Aussi, à quoi servent réellement les opérateurs de bits? J'apprécierais quelques exemples.

N'a été que partiellement traité. Ce sont mes deux cents sur cette question. 

Les opérations au niveau des bits dans les langages de programmation jouent un rôle fondamental dans le traitement de nombreuses applications. Presque toute l'informatique de bas niveau doit être réalisée à l'aide de ce type d'opérations. 

Dans toutes les applications nécessitant l'envoi de données entre deux nœuds, telles que:

  • réseaux informatiques;

  • applications de télécommunication (téléphones cellulaires, communications par satellite, etc.).

Dans la couche de communication de niveau inférieur, les données sont généralement envoyées sous la forme frames. Les cadres ne sont que des chaînes d'octets envoyées via un canal physique. Ces cadres contiennent généralement les données réelles ainsi que d’autres champs (codés en octets) qui font partie de ce que l’on appelle le header. L’en-tête contient généralement des octets qui codent certaines informations relatives à l’état de la communication (avec des indicateurs (bits)), des compteurs de trames, des codes de correction et de détection d’erreur, etc. Pour obtenir les données transmises dans une trame et construire le cadres pour envoyer des données, vous aurez besoin d’opérations au niveau du bit.

En général, lorsque vous utilisez ce type d’application, une API est disponible pour que vous n’ayez pas à traiter tous ces détails. Par exemple, tous les langages de programmation modernes fournissent des bibliothèques pour les connexions socket, vous n'avez donc pas besoin de créer les trames de communication TCP/IP. Mais pensez aux personnes compétentes qui ont programmé ces API pour vous, elles étaient sûrement confrontées à la construction de cadres; en utilisant toutes sortes d'opérations au niveau des bits pour passer de la communication de bas niveau à la communication de niveau supérieur.

A titre d'exemple concret, imaginons que quelqu'un vous donne un fichier contenant des données brutes capturées directement par le matériel de télécommunication. Dans ce cas, pour trouver les cadres, vous devez lire les octets bruts du fichier et essayer de trouver un type de mot de synchronisation en balayant les données bit par bit. Après avoir identifié les mots de synchronisation, vous devrez obtenir les images réelles et LES D&EACUTE;PLACER si nécessaire (et ce n’est que le début de l’histoire) pour obtenir les données effectivement transmises.

Une autre famille d’applications de niveau inférieur très différente s’applique lorsque vous devez contrôler du matériel à l’aide de certains (anciens) types de ports, tels que les ports parallèles et série. Ces ports sont contrôlés en définissant des octets, et chaque bit de ces octets a une signification spécifique, en termes d'instructions, pour ce port (voir par exemple http://en.wikipedia.org/wiki/Parallel_port ) . Si vous voulez créer un logiciel qui fait quelque chose avec ce matériel, vous aurez besoin d'opérations au niveau du bit pour traduire les instructions à exécuter en octets compris par le port.

Par exemple, si vous avez des boutons physiques connectés au port parallèle pour contrôler un autre périphérique, il s'agit d'une ligne de code que vous pouvez trouver dans l'application logicielle: 

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

J'espère que cela contribue.

9
eguaio

J'espère que cela clarifie les deux:

x | 2

0001 //x
0010 //2

0011 //result = 3

x & 1

0001 //x
0001 //1

0001 //result = 1
6
Amarghosh

Pensez à 0 comme faux et à 1 comme vrai. Ensuite, au niveau des bits et (&) et ou (|) fonctionnent exactement comme d'habitude et et ou ou sauf qu'ils font tous les bits de la valeur en même temps. En règle générale, vous les verrez utilisés pour les indicateurs si vous avez 30 options pouvant être définies (par exemple, comme styles de dessin sur une fenêtre), vous ne voulez pas avoir à passer 30 valeurs booléennes distinctes pour définir ou désélectionner chacune d'entre elles afin que vous utilisiez | pour combiner des options en une valeur unique, puis utilisez & pour vérifier si chaque option est définie. Ce style de passage de drapeau est très utilisé par OpenGL. Comme chaque bit est un drapeau distinct, vous obtenez des valeurs de drapeau pour des puissances de deux (autrement dit, les nombres n’ayant qu’un bit défini) 1 (2 ^ 0) 2 (2 ^ 1) 4 (2 ^ 2) 8 (2 ^ 3) la puissance de deux vous indique quel bit est activé si le drapeau est activé.

Notez également 2 = 10 alors x | 2 est 110 (6) pas 111 (7) Si aucun des bits ne se chevauche (ce qui est vrai dans ce cas) | agit comme addition.

5
stonemetal

Je ne l'ai pas vu mentionné ci-dessus, mais vous verrez également certaines personnes utiliser le décalage gauche et droit pour les opérations arithmétiques. Un décalage gauche de x équivaut à une multiplication par 2 ^ x (tant qu'il ne déborde pas) et un décalage droit équivaut à une division par 2 ^ x.

Récemment, j'ai vu des gens utiliser x << 1 et x >> 1 pour doubler et réduire de moitié, bien que je ne sois pas sûr s'ils essaient juste d'être intelligent ou s'il y a vraiment un avantage distinct sur les opérateurs normaux.

5
P. Stallworth

Cet exemple vous montrera les opérations pour les quatre valeurs à 2 bits:

10 | 12

1010 #decimal 10
1100 #decimal 12

1110 #result = 14

10 & 12

1010 #decimal 10
1100 #decimal 12

1000 #result = 8

Voici un exemple d'utilisation:

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

Un autre cas d'utilisation courant est la manipulation/test des autorisations de fichiers. Voir le module stat Python: http://docs.python.org/library/stat.html .

Par exemple, pour comparer les autorisations d'un fichier à un ensemble d'autorisations souhaité, vous pouvez effectuer les opérations suivantes:

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)

Je présente les résultats sous forme de valeurs booléennes, car je me soucie uniquement de la vérité ou du mensonge, mais il serait intéressant de pouvoir imprimer les valeurs bin () de chaque résultat.

2
dbn

Les représentations binaires d’entiers sont souvent utilisées en informatique scientifique pour représenter des tableaux d’informations vrai-faux, car une opération au niveau du bit est beaucoup plus rapide que de parcourir un tableau de booléens. (Les langues de niveau supérieur peuvent utiliser l'idée d'un tableau de bits.)

Un bon exemple assez simple en est la solution générale au jeu de Nim. Jetez un coup d'oeil au Python code sur la page Wikipedia . Il fait un usage intensif de bits exclusif, ou ^.

2
Jonah

je ne l'ai pas vu mentionné, cet exemple vous montrera l'opération décimale (-) pour les valeurs à 2 bits: A-B (uniquement si A contient B)

cette opération est nécessaire lorsque nous avons dans notre programme un verbe qui représente des bits. parfois nous avons besoin d'ajouter des bits (comme ci-dessus) et parfois nous devons supprimer des bits (si le verbe en contient alors)

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

avec python: 7 & ~ 4 = 3 (retirez de 7 les bits qui représentent 4)

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

avec python: 1 & ~ 4 = 1 (retirez de 1 les bits représentant 4 - dans ce cas, 1 n'est pas 'contient' 4) ..

1
Eyal Ch

Il existe peut-être un meilleur moyen de localiser un élément de tableau entre deux valeurs, mais comme le montre cet exemple, le&fonctionne ici, alors queetne fonctionne pas.

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
woodfoot

Ensembles

Les ensembles peuvent être combinés à l'aide d'opérations mathématiques.

  • L'opérateur d'union | combine deux ensembles pour en former un nouveau contenant des éléments. 
  • L'opérateur d'intersection & obtient les éléments uniquement dans les deux cas. 
  • L'opérateur de différence - récupère les éléments dans le premier ensemble, mais pas dans le second. 
  • L'opérateur de différence symétrique ^ récupère les éléments dans l'un ou l'autre des ensembles, mais pas les deux.

Essayez vous-même:

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)

Résultat:

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

{4, 5, 6}

{1, 2, 3}

{8, 9, 7}

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

les opérateurs de bits suivants: & , | , ^ , et ~ renvoient des valeurs (en fonction de leur entrée) de la même manière portes logiques affectent des signaux. Vous pouvez les utiliser pour émuler des circuits.

0
Erik Waters

Pour retourner des bits (c'est-à-dire complément/invert de 1), vous pouvez effectuer les opérations suivantes:

Comme la valeur ExORed avec tous les 1 entraîne l’inversion, vous pouvez utiliser ExOR pour une largeur de bit donnée.

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'
0
user11034079

Bien que la manipulation des bits d'un entier soit utile, souvent pour les protocoles réseau, qui peuvent être spécifiés jusqu'au bit, il peut être nécessaire de manipuler des séquences d'octets plus longues (qui ne sont pas facilement converties en un seul entier). Dans ce cas, il est utile d’utiliser la bibliothèque bitstring qui permet des opérations au niveau des bits sur des données - par exemple. on peut importer la chaîne 'ABCDEFGHIJKLMNOPQ' en tant que chaîne ou en tant qu'hex et la décaler (ou effectuer d'autres opérations au niveau du bit):

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