91

Is there a built in function in python which will convert a binary string, for example '111111111111', to the two's complement integer -1?

erikbstack
  • 12,878
  • 21
  • 81
  • 115
Jim
  • 911
  • 1
  • 7
  • 3
  • @CeesTimmerman Though it's good practice, users are not required to accept an answer. It's also unlikely that the inactive OP will ever see your comment. – mbomb007 Feb 21 '17 at 16:47
  • @mbomb007 True, but unaccepted answers waste time of people thinking there's no correct answer yet. – Cees Timmerman Feb 22 '17 at 09:53
  • 1
    @CeesTimmerman Only if you think that way. Change your thinking. People's votes represent what they think is a correct answer. The accepted answer is basically equivalent to one vote by the OP. It's just that. One vote. – mbomb007 Feb 22 '17 at 14:27
  • 1
    @mbomb007 In the search listings it's not obvious when questions are answered correctly unless they've been marked as such. – Cees Timmerman Feb 22 '17 at 15:10
  • 2
    I've seen plenty of wrong accepted answers. So even then, it's not obvious. http://meta.stackexchange.com/a/26641/285610 – mbomb007 Feb 22 '17 at 15:19
  • @CeesTimmerman On the one hand, I agree that it's nice for the OP to accept an answer, and I often encourage new posters to accept (even on questions I haven't answered). OTOH, _many_ questions are asked by low-rep OPs, and of all the SO members that visit their question the OP may be the _least_ qualified person to decide which is the best answer. – PM 2Ring Apr 22 '17 at 09:17
  • (cont) So (IMHO) an accept is _not_ the same as a vote. At best, it indicates that the answer is the OP's favourite, and that (hopefully) it solved their problem. At worst, it skews the perception of people visiting the page who naturally assume that the answer at the top with the big green answer must be the best one when in a large number of cases it most certainly isn't. – PM 2Ring Apr 22 '17 at 09:18
  • OP appears to be still looking for a solution to his problem, despite the ratings indicating there is one for the stated question. – Cees Timmerman Apr 23 '17 at 05:59
  • Constraint: **should approaches assume the string only contains '1's (and maybe 0's'), no spacing characters like commas, apostrophes, spaces or underscores** (which are very commmonly used for digit-group spacing in long binary/hex strings, not just in Python, but e.g. in languages like Verilog)? And no binary point either? – smci Oct 20 '20 at 23:40
  • Also, do you want two's complement to length 32b, 64b, 128b, 16b or whatever arbitrary length the input string is, rounded up to next 32b or next-highest-power-of-2? – smci Oct 20 '20 at 23:48

20 Answers20

100

Two's complement subtracts off (1<<bits) if the highest bit is 1. Taking 8 bits for example, this gives a range of 127 to -128.

A function for two's complement of an 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

Going from a binary string is particularly easy...

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

A bit more useful to me is going from hex values (32 bits in this example)...

hex_string = '0xFFFFFFFF' # or whatever... '0x' prefix doesn't matter
out = twos_comp(int(hex_string,16), 32)
Fritz
  • 1,293
  • 15
  • 27
travc
  • 1,788
  • 1
  • 18
  • 9
  • @Likak, can you elaborate on that comment? – larsks Feb 06 '17 at 02:03
  • @Likak, the answer is ok. See also [Subtraction from 2^N](https://en.wikipedia.org/wiki/Two's_complement#Subtraction_from_2N) for the reasoning behind it. – maxschlepzig Feb 26 '17 at 11:06
  • 1
    `return val & ((2 ** bits) - 1)` Without this, you'll just get a negative number in normal python formatting. Presumably when doing 2sc you would want the bits. – TechnoSam Oct 12 '17 at 03:09
  • @TechnoSam We want a normal python integer. The 2's compliment means negative (high bit set), it should be negative. That's the whole point. – travc Dec 19 '17 at 00:48
  • i dont think you need != 0 since if statement automatically passes variable to __bool__ or __nonzero__ method so if it is bigger than 0 or not None it will pass. – Danilo May 01 '18 at 16:27
  • 1
    @Danilo Yep, `!= 0` is not needed, but a good practice to include. That function could even be reduced down to a one-liner, but it wouldn't be as clear ;) – travc May 02 '18 at 07:00
  • @travc i think ternary operations are quite clear, but i understand that you've written this for all kind of newbies to understand, so i get it. But , and i wouldn't like to impose my opinion, i kinda think that it could be mentioned. Which you did in comment, so anyone who is interested could look it up. – Danilo May 02 '18 at 07:07
  • int('0b1111', 2) works fine, or this for that matter int('0o1234', 8) – skvalen Feb 26 '19 at 07:45
  • Oh, this is for converting it the other way round – Heath Mitchell Mar 13 '21 at 22:36
31

Since Python 3.2, there are built-in functions for byte manipulation: https://docs.python.org/3/library/stdtypes.html#int.to_bytes.

By combining to_bytes and from_bytes, you get

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)

Check:

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

For older versions of Python, travc's answer is good but it does not work for negative values if one would like to work with integers instead of strings. A twos' complement function for which f(f(val)) == val is true for each val is:

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
PhilR
  • 5,375
  • 1
  • 21
  • 27
22

It's not built in, but if you want unusual length numbers then you could use the bitstring module.

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

The same object can equivalently be created in several ways, including

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

It just behaves like a string of bits of arbitrary length, and uses properties to get different interpretations:

>>> print a.int, a.uint, a.bin, a.hex, a.oct
-1 4095 111111111111 fff 7777
Scott Griffiths
  • 21,438
  • 8
  • 55
  • 85
  • 1
    A comparison between different bit-handling tools can be [found here](http://stackoverflow.com/a/20846054/131120) – erikbstack Oct 23 '14 at 13:38
  • 2
    @erikb85: The answers there (mine included) didn't really get into the simplicity and flexibility of the interface (the OP even started off by complaining that `bitarray` does more than he needs…), so these questions make a nice complement to each other: this one shows how libraries like `bitstring` make common operations easier to write, that one shows they don't make them faster, and often make them slower. – abarnert Oct 23 '14 at 17:42
12
>>> bits_in_word=12
>>> int('111111111111',2)-(1<<bits_in_word)
-1

This works because:

The two's complement of a binary number is defined as the value obtained by subtracting the number from a large power of two (specifically, from 2^N for an N-bit two's complement). The two's complement of the number then behaves like the negative of the original number in most arithmetic, and it can coexist with positive numbers in a natural way.

John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • It's wrong -> check for '000' and it gives -8 – quqa123 Mar 25 '21 at 21:51
  • 2
    @quqa123 Before applying this method, one must examine whether the value is negative in the first place. If the value is smaller or equal to `(1 << (bits_in_word - 1)) - 1`, it is positive and should not be subject to this method. – Fanchen Bao Nov 07 '21 at 20:04
9

This will give you the two's complement efficiently using bitwise logic:

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))
    return value - int((value << 1) & 2**bitWidth)

How it works:

First, we make sure that the user has passed us a value that is within the range of the supplied bit range (e.g. someone gives us 0xFFFF and specifies 8 bits) Another solution to that problem would be to bitwise AND (&) the value with (2**bitWidth)-1

To get the result, the value is shifted by 1 bit to the left. This moves the MSB of the value (the sign bit) into position to be anded with 2**bitWidth. When the sign bit is '0' the subtrahend becomes 0 and the result is value - 0. When the sign bit is '1' the subtrahend becomes 2**bitWidth and the result is value - 2**bitWidth

Example 1: If the parameters are value=0xFF (255d, b11111111) and bitWidth=8

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

Example 2: If the parameters are value=0x1F (31d, b11111) and bitWidth=6

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

Example 3: value = 0x80, bitWidth = 7

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

Example 4: value = 0x80, bitWitdh = 8

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

Now, using what others have already posted, pass your bitstring into int(bitstring, 2) and pass to the twos_complement method's value parameter.

Intrastellar Explorer
  • 3,005
  • 9
  • 52
  • 119
Tom Myddeltyn
  • 1,307
  • 1
  • 13
  • 27
4

A couple of implementations (just an illustration, not intended for use):

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
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • If you're already handling binary as strings, why wouldn't you use this clear and flexible function to create signed integers from them? – Cees Timmerman Apr 23 '17 at 06:10
  • @CeesTimmerman perhaps I meant that "01"-string is a poor representation for an integer (for arithmetic) and therefore functions that manipulate them directly shouldn't be used. – jfs Apr 23 '17 at 11:07
3

in case someone needs the inverse direction too:

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)

should output this: 0111 0110 0101 0100 0011 0010 0001 0000 1111 1110 1101 1100 1011 1010 1001 1000

  • n bits two's complement binary number x is represented as the positive (2 to the power n)+x. example: x=-2, n=4, (2 to the power 4) + (-2) = 14, bin=1110 – Rumpelstiltskin Koriat Feb 04 '17 at 11:27
  • 1
    Since you're doing bit manipulations you should use `1 << wordsize` rather than `2 ** wordsize`; besides, bit shifting is significantly faster than exponentiation. – PM 2Ring Apr 22 '17 at 07:17
3

No, there is no builtin function that converts two's complement binary strings into decimals.

A simple user defined function that does this:

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)

Note that this function doesn't take the bit width as parameter, instead positive input values have to be specified with one or more leading zero bits.

Examples:

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
maxschlepzig
  • 35,645
  • 14
  • 145
  • 182
3

You can use the bit_length() function to convert numbers to their two's complement:

def twos_complement(j):
   return j-(1<<(j.bit_length()))

In [1]: twos_complement(0b111111111111)                                                                                                                                                             
Out[1]: -1
oppressionslayer
  • 6,942
  • 2
  • 7
  • 24
2

you could convert the integer to bytes and then use struct.unpack to convert:

from struct import unpack

x = unpack("b", 0b11111111.to_bytes(length=1, byteorder="little"))
print(x)  # (-1,)
hiro protagonist
  • 44,693
  • 14
  • 86
  • 111
1

Since erikb85 brought up performance, here's travc's answer against 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

So, bitstring is, as found in the other question, almost an order of magnitude slower than int. But on the other hand, it's hard to beat the simplicity—I'm converting a uint to a bit-string then to an int; you'd have to work hard not to understand this, or to find anywhere to introduce a bug. And as Scott Griffiths' answer implies, there's a lot more flexibility to the class which might be useful to the same app. But on the third hand, travc's answer makes it clear what's actually happening—even a novice should be able to understand what conversion from an unsigned int to a 2s complement signed int means just from reading 2 lines of code.

Anyway, unlike the other question, which was about directly manipulating bits, this one is all about doing arithmetic on fixed-length ints, just oddly-sized ones. So I'm guessing if you need performance, it's probably because you have a whole bunch of these things, so you probably want it to be vectorized. Adapting travc's answer to 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

Now:

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

You could probably beat that with custom C code, but you probably don't have to.

Community
  • 1
  • 1
abarnert
  • 354,177
  • 51
  • 601
  • 671
1

Unfortunately there is no built-in function to cast an unsigned integer to a two's complement signed value, but we can define a function to do so using bitwise operations:

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

The first bitwise-and operation is used to sign-extend negative numbers (most significant bit is set), while the second is used to grab the remaining 11 bits. This works since integers in Python are treated as arbitrary precision two's complement values.

You can then combine this with the int function to convert a string of binary digits into the unsigned integer form, then interpret it as a 12-bit signed value.

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

One nice property of this function is that it's idempotent, thus the value of an already signed value will not change.

>>> s12(-1)
-1
dcoles
  • 3,785
  • 2
  • 28
  • 26
  • Why 11 bits? The given string was just one example. – Cees Timmerman Apr 23 '17 at 07:42
  • For this question, it was assumed the author was asking how to interpret 12 binary digits as a 12-bit two's complement signed integer (since -1 is always represented by N 1-bits in an N-bit two's complement representation). The first bit is used for the sign, while the remaining (11) bits determine the magnitude. – dcoles Apr 25 '17 at 01:21
1

Use ~, ^, and a mask (mask determines the total number of bits)

# Given negative value, obtain its two's complement form in 16 bits

>>> mask = (1 << 16) - 1
>>> a = -6
>>> bin(~(a ^ mask))
'0b1111111111111010'
# Given 16-bit signed binary string, return the integer value
>>> mask = (1 << 16) - 1
>>> b = '1111111111110101'
>>> ~(int(b, 2) ^ mask)
-11
Fanchen Bao
  • 3,310
  • 1
  • 21
  • 34
0

I'm using Python 3.4.0

In Python 3 we have some problems with data types transformation.

So... here I'll tell a tip for those (like me) that works a lot with hex strings.

I'll take an hex data and make an complement it:

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 or -0x5352fef7 or '-0b1010011010100101111111011110111'

SnowBG
  • 89
  • 1
  • 2
  • 12
0

This works for 3 bytes. Live code is here

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
D.Deriso
  • 4,271
  • 2
  • 21
  • 14
0

Ok i had this issue with uLaw compression algorithm with PCM wav file type. And what i've found out is that two's complement is kinda making a negative value of some binary number as can be seen here.And after consulting with wikipedia i deemed it true.

The guy explained it as finding least significant bit and flipping all after it. I must say that all these solutions above didn't help me much. When i tried on 0x67ff it gave me some off result instead of -26623. Now solutions may have worked if someone knew the least significant bit is scanning list of data but i didn't knew since data in PCM varies. So here is my answer:

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

Now since code is unreadable i will walk you through the idea.

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

This is just any hexadecimal value, i needed test to be sure but in general it could be anything in range of int. So not to loop over whole bunch of 65535 values short integer can have i decided to split it by nibbles ( 4 bits ). It could be done like this if you haven't used bitwise operators before.

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 

So we are searching for least significant bit so here the min(valid_nibble ) will suffice. Here we've gotten the place where first active (with setted bit) nibble is. Now we just need is to find where in desired nibble is our first setted bit.

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

Now here i need to clarify somethings since seeing ~ and ^ can confuse people who aren't used to this:

XOR: ^: 2 numbers are necesery

This operation is kinda illogical, for each 2 bits it scans if both are either 1 or 0 it will be 0, for everything else 1.

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

And another example:

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

1's complement : ~ - doesn't need any other number

This operation flips every bit in a number. It is very similar to what we are after but it doesn't leave the least significant bit.

0b10110  
~  
0b01001

And as we can see here 1's compliment is same as number XOR full set bits.


Now that we've understood each other, we will getting two's complement by restoring all bites to least significant bit in one's complement.

data = ~data # one's complement of data 

This unfortunately flipped all bits in our number, so we just need to find a way to flip back the numbers we want. We can do that with bit_shift since it is bit position of our bit we need to keep. So when calculating number of data some number of bits can hold we can do that with 2**n and for nibble we get 16 since we are calculating 0 in values of bits.

2**4 = 16 # in binary 1 0000 

But we need the bytes after the 1 so we can use that to diminish the value by 1 and we can get.

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

So lets see the logic in concrete example:

 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

I hope this help'd you or any newbie that had this same problem and researched their a** off finding the solution. Have in mind this code i wrote is frankenstein code , that i why i had to explain it. It could be done more prettier, if anyone wants to make my code pretty please be my guest.

Danilo
  • 1,017
  • 13
  • 32
0

Here's a version to convert each value in a hex string to it's two's complement version.


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            
oppressionslayer
  • 6,942
  • 2
  • 7
  • 24
0

Still a very relevant question, but none of the answers worked in my scenario - which is surprising.

Here's a very simple function to calculate n-bit 2's complement integer values from an integer value.

This function especially ensures that the returned value is NOT seen as a negative value by python, as it breaks the very nature of 2's complement numbers.

2's complement numbers were originally created to work with both positive and negative values on architectures that do not natively support them. It's a conversion and is all about using the bits available to represent and calculate with both positive and negative numbers.

Therefore the number of bits can be specified, defaults to 16, and can by setting it to 0, be set to the required number of bits for the given value.

    def twos_comp(val, bits=16):
        """compute the 2's complement of int value """
        if bits == 0:      # Use as many bits needed for the value.
            bits = val.bit_length()
        return ((val & (2 ** bits) - 1) - (2 ** bits)) * -1

Test code:

    value = 6752
    print(f'{value:05d} = 0x{value:04x} = 0b{value:016b}')
    
    value = twos_comp(value)
    print(f'{value:05d} = 0x{value:04x} = 0b{value:016b}')
    
    value = twos_comp(value)
    print(f'{value:05d} = 0x{value:04x} = 0b{value:016b}')

Test code output:

 06752 = 0x1a60 = 0b0001101001100000
 01440 = 0x05a0 = 0b0000010110100000
 06752 = 0x1a60 = 0b0001101001100000
fsteff
  • 543
  • 5
  • 19
0

Subtract 2^nbits from negative values:

In [75]: (lambda x: int(x,2) - 2**len(x)*int(x[0]))('10000000')
Out[75]: -128

In [76]: (lambda x: int(x,2) - 2**len(x)*int(x[0]))('01111111')
Out[76]: 127

In [77]: (lambda x: int(x,2) - 2**len(x)*int(x[0]))('0111')
Out[77]: 7

In [78]: (lambda x: int(x,2) - 2**len(x)*int(x[0]))('1111001')
Out[78]: -7
  • works for any length input
  • handles positive and negative values correctly
  • NOTE: positive values must have leading zeros
Alex Shroyer
  • 3,499
  • 2
  • 28
  • 54
-2

It's much easier than all that...

for X on N bits: Comp = (-X) & (2**N - 1)

def twoComplement(number, nBits):
    return (-number) & (2**nBits - 1)
Francois
  • 1
  • 1
  • This doesn't work for '1111'. The result of `twoComplement(int('1111', 2), 4)` is `1` when using your function. But the correct result is `-1`. See also the other answers that get this right. – maxschlepzig Feb 26 '17 at 11:51
  • Last time I checked, the 2 complement of 1111 on four bits is 0001. – Francois Feb 27 '17 at 13:48
  • I think you might be confusing calculating the 2 complement of a number with the value of a number (as a negative) in a 2 complement domain. – Francois Feb 27 '17 at 13:49
  • Don't be silly - the term "Two's complement" commonly denotes a decimal interpretation of a bit pattern - cf. e.g. the first 2 tables in https://en.wikipedia.org/wiki/Two's_complement . Also, the OP specifically asked about this and gave as example `'111111111111' -> -1`. That means that you don't answer the question. Thus, your statement "It's much easier than all that ..." doesn't apply. – maxschlepzig Feb 28 '17 at 08:38
  • ahahah, I guess you're right. I must have jumped in the middle of the thread and missed the original question ! – Francois Feb 28 '17 at 15:56