web-dev-qa-db-fra.com

Complément Deux en Python

Existe-t-il une fonction intégrée en python qui convertira une chaîne binaire, par exemple '111111111111', en entier complément de deux -1? 

51
Jim

Le complément à deux soustrait (1<<bits) si le bit le plus élevé est 1. En prenant 8 bits par exemple, cela donne une plage de 127 à -128.

Une fonction pour complément à deux d'un int ...

def twos_comp(val, bits):
    """compute the 2's complement of int value val"""
    if (val & (1 << (bits - 1))) != 0: # if sign bit is set e.g., 8bit: 128-255
        val = val - (1 << bits)        # compute negative value
    return val                         # return positive value as is

Passer d'une chaîne binaire est particulièrement facile ...

binary_string = '1111' # or whatever... no '0b' prefix
out = twos_comp(int(binary_string,2), len(binary_string))

Un peu plus utile pour moi est de passer des valeurs hexadécimales (32 bits dans cet exemple) ...

hex_string = '0xFFFFFFFF' # or whatever... '0x' prefix doesn't matter
out = twos_comp(int(hex_string,16), 32)
61
travc

Il n'est pas intégré, mais si vous voulez des nombres de longueur inhabituels, vous pouvez utiliser le module bitstring .

>>> from bitstring import Bits
>>> a = Bits(bin='111111111111')
>>> a.int
-1

Le même objet peut être créé de différentes manières, notamment:

>>> b = Bits(int=-1, length=12)

Il se comporte simplement comme une chaîne de bits de longueur arbitraire et utilise des propriétés pour obtenir différentes interprétations:

>>> print a.int, a.uint, a.bin, a.hex, a.oct
-1 4095 111111111111 fff 7777
19
Scott Griffiths
>>> bits_in_Word=12
>>> int('111111111111',2)-(1<<bits_in_Word)
-1

Cela fonctionne parce que:

Le complément à deux d'un binaire nombre est défini comme la valeur obtenu en soustrayant le nombre d'une grande puissance de deux (spécifiquement, à partir de 2 ^ N pour un complément à deux N-bits ). Les deux complément du nombre se comporte alors comme le négatif de l'original nombre dans la plupart des calculs, et il peut coexister avec des nombres positifs dans un Manière naturelle.

8
John La Rooy

Depuis Python 3.2, il existe des fonctions intégrées pour la manipulation d'octets: https://docs.python.org/3.4/library/stdtypes.html#int.to_bytes .

En combinant to_bytes et from_bytes, vous obtenez

def twos(val_str, bytes):
    import sys
    val = int(val_str, 2)
    b = val.to_bytes(bytes, byteorder=sys.byteorder, signed=False)                                                          
    return int.from_bytes(b, byteorder=sys.byteorder, signed=True)

Vérifier:

twos('11111111', 1)  # gives -1
twos('01111111', 1)  # gives 127

Pour les anciennes versions de Python, la réponse de travc est bonne, mais elle ne fonctionne pas pour les valeurs négatives si l'on souhaite utiliser des entiers au lieu de chaînes. Fonction du complément à deux pour laquelle f(f(val)) == val est vrai pour chaque valeur valant:

def twos_complement(val, nbits):
    """Compute the 2's complement of int value val"""
    if val < 0:
        val = (1 << nbits) + val
    else:
        if (val & (1 << (nbits - 1))) != 0:
            # If sign bit is set.
            # compute negative value.
            val = val - (1 << nbits)
    return val
8
Gaël Ecorchard

Cela vous donnera le complément à deux efficacement en utilisant la logique de bits:

def twos_complement(value, bitWidth):
    if value >= 2**bitWidth:
        # This catches when someone tries to give a value that is out of range
        raise ValueError("Value: {} out of range of {}-bit value.".format(value, bitWidth))
    else:
        return value - int((value << 1) & 2**bitWidth)

Comment ça marche:

Premièrement, nous nous assurons que l'utilisateur nous a transmis une valeur comprise dans la plage de la plage de bits fournie (par exemple, quelqu'un nous donne 0xFFFF et spécifie 8 bits). Une autre solution à ce problème serait de bitwise AND (2 ** largeur de bit) -1 

Pour obtenir le résultat, la valeur est décalée d'un bit vers la gauche. Cela déplace le bit de poids fort de la valeur (le bit de signe) en position à anded avec 2**bitWidth. Lorsque le bit de signe est '0', la soustraction devient 0 et le résultat est value - 0. Lorsque le bit de signe est '1', la soustraction devient 2**bitWidth et le résultat est value - 2**bitWidth 

Exemple 1: Si les paramètres sont value = 0xFF (255d, b11111111) et bitWidth = 8

  1. 0xFF - int ((0xFF << 1) & 2 ** 8)
  2. 0xFF - int ((0x1FE) & 0x100)
  3. 0xFF - int (0x100)
  4. 255 - 256
  5. -1

Exemple 2: Si les paramètres sont value = 0x1F (31d, b11111) et bitWidth = 6

  1. 0x1F - int ((0x1F << 1) & 2 ** 6)
  2. 0x1F - int ((0x3E) & 0x40)
  3. 0x1F - int (0x00)
  4. 31 - 0
  5. 31

Exemple 3: valeur = 0x80, largeur de bit = 7

ValueError: Value: 128 out of range of 7-bit value.

Exemple 4: valeur = 0x80, bitWitdh = 8

  1. 0x80 - int ((0x80 << 1) & 2 ** 8)
  2. 0x80 - int ((0x100) & 0x100)
  3. 0x80 - int (0x100)
  4. 128 - 256
  5. -128

A présent, en utilisant ce que d’autres ont déjà publié, passez votre chaîne de bits à int (bitstring, 2) et au paramètre de valeur de la méthode twos_complement.

3
Tom Myddeltyn

Quelques implémentations (juste une illustration, non destinée à être utilisée):

def to_int(bin):
    x = int(bin, 2)
    if bin[0] == '1': # "sign bit", big-endian
       x -= 2**len(bin)
    return x

def to_int(bin): # from definition
    n = 0
    for i, b in enumerate(reversed(bin)):
        if b == '1':
           if i != (len(bin)-1):
              n += 2**i
           else: # MSB
              n -= 2**i 
    return n
3
jfs

Depuis qu'erikb85 a amélioré ses performances, voici la réponse de travc contre (Scott Griffiths ' }:

In [534]: a = [0b111111111111, 0b100000000000, 0b1, 0] * 1000
In [535]: %timeit [twos_comp(x, 12) for x in a]
100 loops, best of 3: 8.8 ms per loop
In [536]: %timeit [bitstring.Bits(uint=x, length=12).int for x in a]
10 loops, best of 3: 55.9 ms per loop

Ainsi, bitstring est, comme on le voit dans l'autre question , presque un ordre de grandeur plus lent que int. Mais d’autre part, il est difficile de battre la simplicité: je convertis une uint en une chaîne de bits puis une int; vous devez travailler dur pas pour comprendre cela, ou trouver n'importe où pour introduire un bogue. Et comme le suggère la réponse de Scott Griffiths, il y a beaucoup plus de flexibilité dans la classe qui pourrait être utile pour la même application. Mais du troisième côté, la réponse de travc indique clairement ce qui se passe réellement - même un novice devrait être en mesure de comprendre ce que la conversion d’un entier non signé en un complément à 2s signé en entier signifie simplement à la lecture de 2 lignes de code.

Quoi qu’il en soit, contrairement à l’autre question, qui concernait la manipulation directe de bits, celle-ci consiste à faire de l’arithmétique sur des ints de longueur fixe, juste d’intractes de taille. Donc, je suppose que si vous avez besoin de performance, c'est probablement parce que vous avez tout un tas de choses, donc vous voulez probablement que ce soit vectorisé. Adapter la réponse de travc à numpy:

def twos_comp_np(vals, bits):
    """compute the 2's compliment of array of int values vals"""
    vals[vals & (1<<(bits-1)) != 0] -= (1<<bits)
    return vals

À présent:

In [543]: a = np.array(a)
In [544]: %timeit twos_comp_np(a.copy(), 12)
10000 loops, best of 3: 63.5 µs per loop

Vous pourriez probablement battre cela avec du code C personnalisé, mais ce n'est probablement pas nécessaire.

1
abarnert

si quelqu'un a aussi besoin du sens inverse:

def num_to_bin(num, wordsize):
    if num < 0:
        num = 2**wordsize+num
    base = bin(num)[2:]
    padding_size = wordsize - len(base)
    return '0' * padding_size + base

for i in range(7, -9, -1):
    print num_to_bin(i, 4)

devrait produire ceci: .1100 1011 1010 1001 1000

1

Non, aucune fonction intégrée ne permet de convertir complément à deux chaînes binaires en décimales.

Une simple fonction définie par l'utilisateur qui fait ceci:

def two2dec(s):
  if s[0] == '1':
    return -1 * (int(''.join('1' if x == '0' else '0' for x in s), 2) + 1)
  else:
    return int(s, 2)

Notez que cette fonction ne prend pas la largeur en bits en tant que paramètre, mais que les valeurs d'entrée positives doivent être spécifiées avec un ou plusieurs bits nuls en tête.

Exemples:

In [2]: two2dec('1111')
Out[2]: -1

In [3]: two2dec('111111111111')
Out[3]: -1

In [4]: two2dec('0101')
Out[4]: 5

In [5]: two2dec('10000000')
Out[5]: -128

In [6]: two2dec('11111110')
Out[6]: -2

In [7]: two2dec('01111111')
Out[7]: 127
1
maxschlepzig

Voici une version pour convertir chaque valeur d'une chaîne hexadécimale en version complémentaire à deux.


In [5159]: twoscomplement('f0079debdd9abe0fdb8adca9dbc89a807b707f')                                                                                                 
Out[5159]: '10097325337652013586346735487680959091'


def twoscomplement(hm): 
   twoscomplement='' 
   for x in range(0,len(hm)): 
       value = int(hm[x],16) 
       if value % 2 == 1: 
         twoscomplement+=hex(value ^ 14)[2:] 
       else: 
         twoscomplement+=hex(((value-1)^15)&0xf)[2:] 
   return twoscomplement            
0
oppressionslayer

J'utilise Python 3.4.0

En Python 3, nous rencontrons des problèmes avec la transformation des types de données.

Alors ... ici, je vais donner un conseil pour ceux (comme moi) qui travaillent beaucoup avec des chaînes hexagonales.

Je vais prendre une donnée hexadécimale et en faire un complément:

a = b'acad0109'

compl = int(a,16)-pow(2,32)

result=hex(compl)
print(result)
print(int(result,16))
print(bin(int(result,16)))

résultat = -1397948151 ou -0x5352fef7 ou '-0b1010011010100111111111111110111'

0
SnowBG

Malheureusement, il n'y a pas de fonction intégrée pour convertir un entier non signé en valeur signée du complément à deux, mais nous pouvons définir une fonction à cet effet à l'aide d'opérations au niveau du bit:

def s12(value):
    return -(value & 0b100000000000) | (value & 0b011111111111)

La première opération sur les bits est utilisée pour signer et étendre les nombres négatifs (le bit le plus significatif est défini), tandis que la seconde est utilisée pour saisir les 11 bits restants. Cela fonctionne car les entiers en Python sont traités comme des valeurs de complément à deux à précision arbitraire.

Vous pouvez ensuite combiner cela avec la fonction int pour convertir une chaîne de chiffres binaires en un entier non signé, puis l'interpréter comme une valeur signée de 12 bits.

>>> s12(int('111111111111', 2))
-1
>>> s12(int('011111111111', 2))
2047
>>> s12(int('100000000000', 2))
-2048

Une propriété de Nice de cette fonction est qu’elle est idempotente, ainsi la valeur d’une valeur déjà signée ne changera pas.

>>> s12(-1)
-1
0
dcoles

Cela fonctionne pour 3 octets. Le code en direct est ici

def twos_compliment(byte_arr):
   a = byte_arr[0]; b = byte_arr[1]; c = byte_arr[2]
   out = ((a<<16)&0xff0000) | ((b<<8)&0xff00) | (c&0xff)
   neg = (a & (1<<7) != 0)  # first bit of a is the "signed bit." if it's a 1, then the value is negative
   if neg: out -= (1 << 24)
   print(hex(a), hex(b), hex(c), neg, out)
   return out


twos_compliment([0x00, 0x00, 0x01])
>>> 1

twos_compliment([0xff,0xff,0xff])
>>> -1

twos_compliment([0b00010010, 0b11010110, 0b10000111])
>>> 1234567

twos_compliment([0b11101101, 0b00101001, 0b01111001])
>>> -1234567

twos_compliment([0b01110100, 0b11001011, 0b10110001])
>>> 7654321

twos_compliment([0b10001011, 0b00110100, 0b01001111])
>>> -7654321
0
D.Deriso

Ok, j'avais ce problème avec l'algorithme de compression uLaw avec type de fichier PCM wav} _. Et ce que j’ai découvert, c’est que le complément à deux est en quelque sorte une valeur négative d’un nombre binaire comme on peut le voir ici et après avoir consulté wikipedia j’ai jugé que c’était vrai. 

Le gars a expliqué que trouver least significant bit et tout retourner après cela. Je dois dire que toutes ces solutions ci-dessus ne m'ont pas beaucoup aidé. Quand j’ai essayé sur 0x67ff, il m’a donné des résultats au lieu de -26623. Maintenant, les solutions ont peut-être fonctionné si quelqu'un savait que la least significant bit scanne la liste des données, mais je ne le savais pas, car les données dans PCM varient. Alors voici ma réponse:

max_data = b'\xff\x67' #maximum value i've got from uLaw data chunk to test

def twos_compliment(short_byte): # 2 bytes 
    short_byte = signedShort(short_byte) # converting binary string to integer from struct.unpack i've just shortened it.
    valid_nibble = min([ x*4 for x in range(4) if (short_byte>>(x*4))&0xf ])
    bit_shift = valid_nibble + min( [ x for x in [1,2,4,8] if ( ( short_byte>>valid_nibble )&0xf )&x ] )
    return (~short_byte)^( 2**bit_shift-1 )

data  = 0x67ff
bit4 = '{0:04b}'.format
bit16 = lambda x: ' '.join( map( bit4, reversed([ x&0xf, (x>>4)&0xf, (x>>8)&0xf, (x>>12)&0xf ]) ) )

# print( bit16(0x67ff) , ' : ', bit16( twos_compliment(  b'\xff\x67' ) ) )
# print( bit16(0x67f0) , ' : ', bit16( twos_compliment(  b'\xf0\x67' ) ) )
# print( bit16(0x6700) , ' : ', bit16( twos_compliment(  b'\x00\x67' ) ) )
# print( bit16(0x6000) , ' : ', bit16( twos_compliment(  b'\x00\x60' ) ) )
print( data, twos_compliment(max_data) )

Maintenant, comme le code est illisible, je vais vous expliquer cette idée. 

## example data, for testing... in general unknown
data = 0x67ff # 26623 or 0110 0111 1111 1111 

C'est n'importe quelle valeur hexadécimale, j'avais besoin d'un test pour en être sûr, mais en général, cela pourrait être n'importe quoi dans la plage de int. Donc, pour ne pas boucler sur tout un tas de 65535 valeurs short integer, j’ai décidé de le scinder en nibbles _ (4 bits). Cela pourrait être fait comme ça si vous n'aviez pas utilisé bitwise operators avant.

nibble_mask = 0xf # 1111
valid_nibble = []

for x in range(4): #0,1,2,3 aka places of bit value
    # for individual bits you could go 1<<x as you will see later

    # x*4 is because we are shifting bit places , so 0xFA>>4 = 0xF
    #     so 0x67ff>>0*4 = 0x67ff
    #     so 0x67ff>>1*4 = 0x67f
    #     so 0x67ff>>2*4 = 0x67
    #     so 0x67ff>>3*4 = 0x6
    # and nibble mask just makes it confided to 1 nibble so 0xFA&0xF=0xA
    if (data>>(x*4))&nibble_mask: valid_nibble.append(x*4) # to avoid multiplying it with 4 later 

Nous cherchons donc least significant bit alors le min(valid_nibble ) suffira. Ici, nous avons obtenu le premier nibble actif (avec le bit réglé). Maintenant, nous avons juste besoin de trouver où est le premier bit réglé dans le grignotage souhaité. 

bit_shift = min(valid_nibble)
for x in range(4): 
    # in my example above [1,2,4,8] i did this to spare python calculating 
    ver_data = data>>min(bit_shift ) # shifting from 0xFABA to lets say 0xFA
    ver_data &= nibble_mask # from 0xFA to 0xA 
    if ver_data&(1<<x): 
        bit_shift += (1<<x)
        break

Maintenant, voici que je dois clarifier quelque chose puisque voir ~ et ^ peut dérouter des personnes qui ne sont pas habituées à ceci: 

XOR : ^: 2 nombres is necesery 

Cette opération est un peu illogique, pour chaque 2 bits analysés, si les deux sont 1 ou 0, ce sera 0, pour tout le reste 1. 

 0b10110
^0b11100
--------- 
 0b01010   

Et un autre exemple: 

 0b10110
^0b11111
---------
 0b01001

1's complement : ~ - n'a pas besoin d'un autre numéro 

Cette opération retourne chaque bit dans un nombre. C'est très similaire à ce que nous recherchons mais cela ne laisse pas le bit le moins significatif

0b10110  
~  
0b01001

Et comme nous pouvons le voir ici, le complément 1 est identique au nombre XOR de bits complets. 


Maintenant que nous nous sommes compris, nous obtiendrons two's complement en restaurant toutes les morsures en bit le moins significatif} dans le complément}. 

data = ~data # one's complement of data 

Malheureusement, cela a retourné tous les bits de notre numéro, nous avons donc besoin de trouver un moyen de renverser les chiffres que nous voulons. Nous pouvons le faire avec bit_shift puisqu'il s'agit de la position de bit que nous devons conserver. Donc, lors du calcul du nombre de données, un certain nombre de bits peut contenir, nous pouvons le faire avec 2**n et pour nibble, nous obtenons 16 puisque nous calculons 0 en valeurs de bits. 

2**4 = 16 # in binary 1 0000 

Mais nous avons besoin des octets après le 1 pour que nous puissions l'utiliser pour diminuer la valeur de 1 et obtenir. 

2**4 -1 = 15 # in binary 0 1111 

Voyons donc la logique dans un exemple concret: 

 0b110110
 lsb = 2 # binary 10 

~0b110110
----------
 0b001001 # here is that 01 we don't like  

 0b001001
^0b000011 # 2**2 = 4 ; 4-1 = 3 in binary 0b11 
--------- 
 0b001010

J'espère que cette aide vous ou tout débutant qui a eu ce même problème et fait des recherches pour trouver la solution. Gardez à l’esprit ce code que j’ai écrit est le code de Frankenstein, c’est pourquoi j’ai dû l’expliquer. Cela pourrait être plus joli, si quelqu'un veut rendre mon code joli, s'il vous plaît, soyez mon invité. 

0
Danilo