web-dev-qa-db-fra.com

Complément Binaire en Python?

Les nombres entiers en Python sont stockés dans le complément à deux, n'est-ce pas?

Bien que:

>>> x = 5
>>> bin(x)
0b101

Et:

>>> x = -5
>>> bin(x)
-0b101

C'est assez boiteux. Comment faire en sorte que python me donne les nombres en bits binaires REELS, et sans le 0b devant? Alors:

>>> x = 5
>>> bin(x)
0101
>>> y = -5
>>> bin(y)
1011
29
Thor Correia

Vous ne savez pas comment obtenir ce que vous voulez en utilisant la bibliothèque standard. Il existe une poignée de scripts et de packages qui effectueront la conversion pour vous.

Je voulais juste noter le "pourquoi", et pourquoi ce n'est pas boiteux.

bin () ne renvoie pas de bits binaires. il convertit le nombre en chaîne binaire. le '0b' initial indique à l'interprète que vous traitez avec un nombre binaire, conformément à la définition du langage python. De cette façon, vous pouvez travailler directement avec des nombres binaires, comme ceci 

>>> 0b01
1
>>> 0b10
2
>>> 0b11
3
>>> 0b01 + 0b10
3

ce n'est pas boiteux. c'est génial.


http://docs.python.org/library/functions.html#bin

bin (x)

Convertir un nombre entier en chaîne binaire. 

http://docs.python.org/reference/lexical_analysis.html#integers

Les littéraux entiers et entiers longs sont décrits par les définitions lexicales suivantes:

bininteger :: = "0" ("b" | "B") bindigit +

bindigit :: = "0" | "1"

2
Jonathan Vanasco

Cela fonctionne mieux si vous fournissez un masque. De cette façon, vous spécifiez jusqu'à quelle distance signer.

>>> bin(-27 & 0b1111111111111111)
'0b1111111111100101'

Ou peut-être plus généralement:

def bindigits(n, bits):
    s = bin(n & int("1"*bits, 2))[2:]
    return ("{0:0>%s}" % (bits)).format(s)

>>> print bindigits(-31337, 24)
111111111000010110010111

En théorie de base, la largeur réelle du nombre est fonction de la taille de la mémoire. S'il s'agit d'un nombre 32 bits, alors un nombre négatif a un 1 dans le bit de poids fort d'un ensemble de 32. S'il s'agit d'une valeur de 64 bits, vous devez afficher 64 bits. 

Mais en Python, la précision des nombres entiers est limitée aux contraintes de votre matériel. Sur mon ordinateur, ceci fonctionne réellement, mais il nécessite 9 Go de RAM pour stocker la valeur de x . Quelque chose de plus élevé et je reçois un MemoryError. Si j'avais plus de RAM, je pourrais stocker de plus grands nombres.

>>> x = 1 << (1 << 36)

Donc, dans cet esprit, quel nombre binaire représente -1? Python est capable d'interpréter littéralement des millions (voire des milliards) de bits de précision, comme le montre l'exemple précédent. Dans le complément à 2, le bit de signe s'étend tout à fait à gauche, mais en Python, il n'y a pas de nombre de bits prédéfini; il y en a autant qu'il vous faut.

Mais vous rencontrez une ambiguïté: le binaire 1 représente-t-il 1 ou -1? Eh bien, ce pourrait être l'un ou l'autre. Est-ce que 111 représente 7 ou -1? Encore une fois, cela pourrait être l'un ou l'autre. De même que 111111111 représente 511 ou -1... eh bien, les deux, en fonction de votre précision. 

Python a besoin d'un moyen de représenter ces nombres en binaire afin d'éviter toute ambiguïté quant à leur signification. Le préfixe 0b indique simplement "ce nombre est en binaire". Tout comme 0x signifie "ce nombre est en hex". Donc, si je dis 0b1111, comment puis-je savoir si l'utilisateur veut -1 ou 15? Il y a deux options:

Option A: le bit de signe _
Vous pouvez déclarer que tous les nombres sont signés et le bit le plus à gauche est le bit de signe. Cela signifie que 0b1 est -1, alors que 0b01 est 1. Cela signifie également que 0b111 est également -1, alors que 0b0111 est 7. En fin de compte, cela est probablement plus déroutant qu'utile, en particulier parce que la plupart des calculs binaires seront non signés de toute façon, et les gens risquent davantage de commettre des erreurs en marquant accidentellement un nombre comme négatif, car ils n'incluaient pas de bit de signe explicite.

Option B: indication du signe
Avec cette option, les nombres binaires sont représentés non signés et les nombres négatifs ont un préfixe "-", comme ils le sont en décimal. Ceci est (a) plus cohérent avec le nombre décimal, (b) plus compatible avec la façon dont les valeurs binaires vont être utilisées. Vous perdez la possibilité de spécifier un nombre négatif à l'aide de la représentation du complément à deux, mais souvenez-vous que le complément à deux est un détail {implémentation de stockage}, et non une indication correcte de la valeur sous-jacente. Cela ne devrait pas être quelque chose que l'utilisateur doit comprendre. 

En fin de compte, l'option B est la plus sensée. Il y a moins de confusion et l'utilisateur n'est pas obligé de comprendre les détails de stockage.

56
tylerl

Pour interpréter correctement une séquence binaire comme un complément à deux, il faut une longueur associée à la séquence. Lorsque vous travaillez avec des types de bas niveau correspondant directement aux registres de la CPU, il existe une longueur implicite. Puisque les entiers Python peuvent avoir une longueur arbitraire, il n’existe pas de format de complément à deux interne. Comme il n'y a pas de longueur associée à un nombre, il n'y a aucun moyen de distinguer les nombres positifs des nombres négatifs. Pour supprimer l'ambiguïté, bin () inclut un signe moins lors du formatage d'un nombre négatif.

Le type entier de longueur arbitraire de Python utilise en fait un format interne de magnitude de signe. Les opérations logiques (décalage de bits, et, ou, etc.) sont conçues pour imiter le format de complément à deux. Ceci est typique de plusieurs bibliothèques de précision.

13
casevh
tobin = lambda x, count=8: "".join(map(lambda y:str((x>>y)&1), range(count-1, -1, -1)))

par exemple.

tobin(5)      # =>  '00000101'
tobin(5, 4)   # =>      '0101'
tobin(-5, 4)  # =>      '1011'

Ou comme des fonctions claires:

# Returns bit y of x (10 base).  i.e. 
# bit 2 of 5 is 1
# bit 1 of 5 is 0
# bit 0 of 5 is 1
def getBit(y, x):
    return str((x>>y)&1)

# Returns the first `count` bits of base 10 integer `x`
def tobin(x, count=8):
    shift = range(count-1, -1, -1)
    bits = map(lambda y: getBit(y, x), shift)
    return "".join(bits)

(Adapté de W.J. Van de Laan commentaire)

3
AJP

Je ne suis pas tout à fait certain de ce que vous voulez faire, mais vous voudrez peut-être examiner le paquet bitarray .

2
user1245262

Pour les nombres positifs, utilisez simplement:

bin(x)[2:].zfill(4)

Pour les nombres négatifs, c'est un peu différent:

bin((eval("0b"+str(int(bin(x)[3:].zfill(4).replace("0","2").replace("1","0").replace("2","1"))))+eval("0b1")))[2:].zfill(4)

En tant que script complet, voici à quoi cela devrait ressembler:

def binary(number):
    if number < 0:
        return bin((eval("0b"+str(int(bin(number)[3:].zfill(4).replace("0","2").replace("1","0").replace("2","1"))))+eval("0b1")))[2:].zfill(4)
    return bin(number)[2:].zfill(4)      
x=input()
print binary(x)
1
PTEC Productions
def tobin(data, width):
    data_str = bin(data & (2**width-1))[2:].zfill(width)
    return data_str
1
Enze Chi

Utilisez des tranches pour vous débarrasser des '0b' indésirables.

bin(5)[2:] '101'

ou si vous voulez des chiffres,

Tuple ( bin(5)[2:] ) ('1', '0', '1')

ou même

map( int, Tuple( bin(5)[2:] ) ) [1, 0, 1]

1
user3181121

Une modification de la réponse très utile de tylerl qui fournit une extension de signe pour les nombres positifs et négatifs (pas de vérification d'erreur).

def to2sCompStr(num, bitWidth):
    num &= (2 << bitWidth-1)-1 # mask
    formatStr = '{:0'+str(bitWidth)+'b}'
    ret =  formatStr.format(int(num))
    return ret

Exemple:

In [11]: to2sCompStr(-24, 18)
Out[11]: '111111111111101000'

In [12]: to2sCompStr(24, 18)
Out[12]: '000000000000011000'
1
pev.hall

Pas besoin, c'est déjà. C'est juste que python a choisi de le représenter différemment. Si vous commencez à imprimer chaque nibble séparément, il affichera ses vraies couleurs.

checkNIB = '{0:04b}'.format
checkBYT = lambda x: '-'.join( map( checkNIB, [ (x>>4)&0xf, x&0xf] ) ) 
checkBTS = lambda x: '-'.join( [ checkBYT( ( x>>(shift*8) )&0xff ) for shift in reversed( range(4) ) if ( x>>(shift*8) )&0xff ] )


print( checkBTS(-0x0002) )

La sortie est simple: 

>>>1111-1111-1111-1111-1111-1111-1111-1110  

Désormais, il revient à la représentation d'origine lorsque vous souhaitez afficher le complément à deux d'un nibble, mais il est toujours possible si vous le divisez en moitiés de nibble, etc. N'oubliez pas que le meilleur résultat est obtenu avec les interprétations négatives sur les nombres hexadécimaux et binaires les nombres simples, mais aussi avec hex vous pouvez définir la taille en octets. 

1
Danilo

Voici une version un peu plus lisible de Tylerl answer , par exemple, disons que vous voulez -2 dans sa représentation négative 8 bits du "complément à deux":

bin(-2 & (2**8-1))

2 ** 8 représente le neuvième bit (256), lui soustrait 1 et tous les bits précédents sont mis à un (255)

pour les masques de 8 et 16 bits, vous pouvez remplacer (2 ** 8-1) par 0xff ou 0xffff. La version hexadécimale devient moins lisible après ce point.

Si cela n’est pas clair, en voici une fonction régulière:

def twosComplement (value, bitLength) :
    return bin(value & (2**bitLength - 1))
0
Nicolas David