Подтвердить что ты не робот

Два дополнения в Python

Есть ли встроенная функция в python, которая преобразует двоичную строку, например '111111111111', в два дополнительных целых числа -1?

4b9b3361

Ответ 1

Два дополнения вычитают (1<<bits), если старший бит равен 1. Принимая 8 бит, например, это дает диапазон от 127 до -128.

Функция для двух дополнений к int...

def twos_comp(val, bits):
    """compute the 2 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

Переход из двоичной строки особенно прост...

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

Немного более полезный для меня идет из шестнадцатеричных значений (32 бита в этом примере)...

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

Ответ 2

Он не встроен, но если вы хотите необычные номера длин, вы можете использовать модуль bitstring.

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

Один и тот же объект может быть сгенерирован несколькими способами, в том числе

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

Он просто ведет себя как строка бит произвольной длины и использует свойства для получения разных интерпретаций:

>>> print a.int, a.uint, a.bin, a.hex, a.oct
-1 4095 111111111111 fff 7777

Ответ 3

Начиная с Python 3.2 существуют встроенные функции для манипулирования байтами: https://docs.python.org/3.4/library/stdtypes.html#int.to_bytes.

Объединив to_bytes и from_bytes, вы получите

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)

Проверьте:

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

Для более старых версий Python ответ travc хорош, но он не работает для отрицательных значений, если вы хотите работать с целыми числами вместо строк. Функция дополнений двойки, для которой f (f (val)) == val истинна для каждого val:

def twos_complement(val, nbits):
    """Compute the 2 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

Ответ 4

>>> bits_in_word=12
>>> int('111111111111',2)-(1<<bits_in_word)
-1

Это работает, потому что:

Два дополнения двоичного число определяется как значение полученных путем вычитания числа от большой мощности двух (в частности, от 2 ^ N для N-разрядного два дополнения). Два дополняет число, затем ведет себя как негатив оригинала число в большинстве арифметических, и оно может сосуществуют с положительными числами в естественным образом.

Ответ 5

Это даст вам два дополнения эффективно с помощью побитовой логики:

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)

Как это работает:

Во-первых, мы удостоверились, что пользователь передал нам значение, находящееся в пределах диапазона поставляемого диапазона бит (например, кто-то дает нам 0xFFFF и указывает 8 бит). Другим решением этой проблемы было бы побитовое И (& ) значение с (2 ** битWidth) -1

Чтобы получить результат, значение сдвигается на 1 бит влево. Это перемещает MSB значения (знаковый бит) в положение, которое должно быть указано с помощью 2**bitWidth. Когда знаковый бит равен "0", вычитание становится равным 0, а результат - value - 0. Когда знаковый бит равен "1", вычитание становится 2**bitWidth, а результат value - 2**bitWidth

Пример 1: Если параметры имеют value = 0xFF (255d, b11111111) и bitWidth = 8

  • 0xFF - int ((0xFF < 1) и 2 ** 8)
  • 0xFF - int ((0x1FE) и 0x100)
  • 0xFF - int (0x100)
  • 255 - 256
  • 1

Пример 2: Если параметры имеют value = 0x1F (31d, b11111) и bitWidth = 6

  • 0x1F - int ((0x1F < 1) и 2 ** 6)
  • 0x1F - int ((0x3E) и 0x40)
  • 0x1F - int (0x00)
  • 31 - 0
  • 31

Пример 3: value = 0x80, битWidth = 7

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

Пример 4: value = 0x80, битWitdh = 8

  • 0x80 - int ((0x80 < 1) и 2 ** 8)
  • 0x80 - int ((0x100) и 0x100)
  • 0x80 - int (0x100)
  • 128 - 256
  • -128

Теперь, используя то, что еще уже опубликовано, передайте свою bitstring в int (bitstring, 2) и перейдите к параметру значения метода twos_complement.

Ответ 6

Несколько реализаций (просто иллюстрация, не предназначенная для использования):

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

Ответ 7

если кто-то нуждается в обратном направлении:

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)

должен выводить это: 0111 0110 0101 0100 0011 0010 0001 0000 1111 1110 1101 1100 1011 1010 1001 1000

Ответ 8

Нет, нет встроенной функции, которая преобразует два дополнения двоичных строк в десятичные числа.

Простая пользовательская функция, которая делает это:

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)

Обратите внимание, что эта функция не принимает ширину бита как параметр, вместо этого положительные входные значения должны указываться одним или несколькими ведущими нулевыми битами.

Примеры:

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

Ответ 9

Так как erikb85 поднял производительность, travc answer против Скотт Гриффитс:

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

Итак, bitstring есть, как указано в другом вопросе, почти на порядок медленнее, чем int. Но, с другой стороны, трудно просто побить простоту - я преобразую a uint в битовую строку, а затем в int; вам придется много работать, чтобы не понимать этого, или найти где-нибудь, чтобы ввести ошибку. И, как следует из ответа Скотта Гриффитса, в классе гораздо больше гибкости, которая может оказаться полезной для одного и того же приложения. Но с другой стороны, ответ travc дает понять, что на самом деле происходит - даже новичок должен уметь понять, какое преобразование из unsigned int в 2s-дополнение, подписанное int, означает только чтение двух строк кода.

Во всяком случае, в отличие от другого вопроса, касающегося непосредственного манипулирования битами, все это касается выполнения арифметики на int-int с фиксированной длиной, только с определенными размерами. Поэтому я предполагаю, что если вам нужна производительность, возможно, потому, что у вас есть целая куча этих вещей, поэтому вы, вероятно, хотите, чтобы она была векторизованной. Адаптация ответа travc на numpy:

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

Сейчас:

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

Возможно, вы могли бы побить это с помощью специального кода на C, но вам, вероятно, не нужно.

Ответ 10

Я использую Python 3.4.0

В Python 3 у нас есть некоторые проблемы с преобразованием типов данных.

Итак... здесь я расскажу совет для тех (как я), которые много работают с шестнадцатеричными строками.

Я возьму шестнадцатеричные данные и сделаю их дополнением:

a = b'acad0109'

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

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

result = -1397948151 или -0x5352fef7 или '-0b1010011010100101111111011110111'

Ответ 11

К сожалению, нет встроенной функции, чтобы отличить целое число без знака к значению с двумя дополнениями, но мы можем определить функцию для этого, используя побитовые операции:

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

Первая побитовая операция и операция используется для увеличения числа отрицательных чисел (самый старший бит установлен), а второй используется для захвата оставшихся 11 бит. Это работает, поскольку целые числа в Python рассматриваются как произвольная точность двух значений дополнения.

Затем вы можете объединить это с помощью функции int, чтобы преобразовать строку двоичных цифр в целочисленную форму без знака, а затем интерпретировать ее как 12-разрядное значение со знаком.

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

Одним из приятных свойств этой функции является то, что она идемпотентна, поэтому значение уже подписанного значения не изменится.

>>> s12(-1)
-1

Ответ 12

Это работает на 3 байта. Живой код здесь

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 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

Ответ 13

Хорошо, у меня была эта проблема с алгоритмом сжатия uLaw с типом файла WAV для PCM. И то, что я узнал, состоит в том, что два дополнения являются своего рода отрицательным значением некоторого двоичного числа, как можно увидеть здесь. И после консультации с wikipedia я счел это правдой.

Парень объяснил это тем, что нашел least significant bit и перевернул все после него. Должен сказать, что все эти решения, приведенные выше, не очень помогли мне. Когда я попытался на 0x67ff он дал мне некоторый результат вместо -26623. Теперь решения, возможно, сработали, если кто-то знал, что least significant bit сканирует список данных, но я не знал, так как данные в PCM варьируются. Итак, вот мой ответ:

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) )

Теперь, так как код не читается, я пройду через эту идею.

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

Это всего лишь шестнадцатеричное значение, мне нужен тест, чтобы быть уверенным, но в целом это может быть что угодно в диапазоне от int. Поэтому, чтобы не перебирать целую цепочку из 65535 значений short integer, я мог бы разделить ее на nibbles (4 бит). Это можно сделать так, если вы раньше не использовали bitwise operators.

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 

Таким образом, мы ищем least significant bit поэтому здесь будет достаточно min(valid_nibble ). Здесь мы получили место, где первый активный (с установленным битом) полубайт. Теперь нам просто нужно найти, где в желаемом грызке находится наш первый установленный бит.

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

Теперь мне нужно прояснить что-то, потому что, видя ~ и ^ могут путать людей, которые не привыкли к этому:

XOR: ^: 2 номера требуются

Эта операция нелогична, для каждого 2 бита она сканирует, если оба они равны 1 или 0, это будет 0, для всего остального 1.

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

И еще один пример:

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

1 complement: ~ - не требуется никакого другого номера

Эта операция переворачивает каждый бит в число. Он очень похож на то, что мы делаем, но он не оставляет наименее значимого бита.

0b10110  
~  
0b01001

И, как мы видим здесь, 1 комплимент такой же, как и полный набор бит XOR.


Теперь, когда мы поняли друг друга, мы получим two complement, восстановив все укусы до наименее значимого бита в одном дополнении.

data = ~data # one complement of data 

Это, к сожалению, перевернуло все биты в нашем номере, поэтому нам просто нужно найти способ перевернуть числа, которые мы хотим. Мы можем сделать это с помощью bit_shift так как это битная позиция нашего бита, которую нам нужно сохранить. Поэтому при вычислении количества данных может быть выполнено некоторое количество бит, мы можем сделать это с помощью 2**n а для nibble получаем 16, так как мы вычисляем 0 в битах.

2**4 = 16 # in binary 1 0000 

Но нам нужны байты после 1 поэтому мы можем использовать это, чтобы уменьшить значение на 1, и мы можем получить.

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

Итак, рассмотрим логику в конкретном примере:

 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

Надеюсь, это поможет поможет вам или новичкам, которые столкнулись с этой проблемой, и исследовали их решение найти решение. Имейте в виду, что этот код, который я написал, - это код франкенштейна, и поэтому я должен был объяснить это. Это можно сделать более красивым, если кто-то хочет сделать мой код симпатичным, пожалуйста, будь моим гостем.

Ответ 14

Здесь версия для преобразования каждого значения в шестнадцатеричной строке в него два дополнения версии.


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            

Ответ 15

Это намного проще, чем все это...

для X на N бит: Comp = (-X) и (2 ** N - 1)

def twoComplement(number, nBits):
    return (-number) & (2**nBits - 1)