views:

1464

answers:

4

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

+1  A: 

But '111111111111' as an integer is 4095. What did you actually mean?

You could always

int('111111111111',2)
Jonathan Feinberg
In two's compliment form isn't 111111111111 = -1 because the first bit is the sign and then the you go backwards from 11111111111 to 00000000000 starting at -1?
Jim
Ok yes, I'm sorry I don't know why I didn't mention that. Is there an built in function that could handle that? I know struct.unpack will work but I can only figure it out for 16 bit or 8 bit words.
Jim
@Jim, that depends on your word size. If yours is 12 bits then, yes, that value is -1. If it's 13 or more bits, the value is *4095*, (not 4096, @Jonathan). If your word size is less than 12 bits, well, I don't know WTH the value is :-)
paxdiablo
Well, Python uses integers significantly larger than the 12 bit integers where 4095 would be equal to -1.(Actually, Python's integers are either 32 bit or "unlimited" in size.)So you'll need to do some bit banging yourself if you want to emulate 12 bit signed integers.
ndim
Thanks devil's peace; corrected.
Jonathan Feinberg
Actually, @Jonathan (not that anyone would care but it may explain my oft-dark humor), it's meant to be "peace with the darker side of my nature" but the Latin for that is unwieldy :-) You're right technically, since it comes from Pax Romana, the Roman peace (which was peace via conquest). The original nym I had was the Greek "ειρήνη με το διάβολο" (ee-ree-nee may to thee-av-a-lo) but, since that predated Unicode on the web, I switched to Latin. There ya go, now you all know what a weirdo I am :-)
paxdiablo
"Peace with the darker side of my nature" is a beautiful idea, and an excellent name.
Jonathan Feinberg
Does "cum diablo pax" work? Ablative case for accompaniment and such.
outis
A: 

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
J.F. Sebastian
+5  A: 
>>> 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.

gnibbler
note: it won't work for '0111..'.
J.F. Sebastian
+4  A: 

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

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

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

>>> b = BitString(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 0b111111111111 0xfff 0o7777
Scott Griffiths