In n-bit two's complement, bits have value:
bit 0 = 20
bit 1 = 21
bit n-2 = 2n-2
bit n-1 = -2n-1
But bit n-1 has value 2n-1 when unsigned, so the number is 2n too high. Subtract 2n if bit n-1 is set:
def twos_complement(hexstr, bits):
value = int(hexstr, 16)
if value & (1 << (bits - 1)):
value -= 1 << bits
return value
print(twos_complement('FFFE', 16))
print(twos_complement('7FFF', 16))
print(twos_complement('7F', 8))
print(twos_complement('FF', 8))
Output:
-2
32767
127
-1
Answer from Mark Tolonen on Stack OverflowIn n-bit two's complement, bits have value:
bit 0 = 20
bit 1 = 21
bit n-2 = 2n-2
bit n-1 = -2n-1
But bit n-1 has value 2n-1 when unsigned, so the number is 2n too high. Subtract 2n if bit n-1 is set:
def twos_complement(hexstr, bits):
value = int(hexstr, 16)
if value & (1 << (bits - 1)):
value -= 1 << bits
return value
print(twos_complement('FFFE', 16))
print(twos_complement('7FFF', 16))
print(twos_complement('7F', 8))
print(twos_complement('FF', 8))
Output:
-2
32767
127
-1
import struct
For Python 3 (with comments' help):
h = '9DA92DAB'
struct.unpack('>i', bytes.fromhex(h))
For Python 2:
h = '9DA92DAB'
struct.unpack('>i', h.decode('hex'))
or if it is little endian:
h = '9DA92DAB'
struct.unpack('<i', h.decode('hex'))
This will do the trick:
>>> print(hex (-1 & 0xffffffff))
0xffffffff
or, a variant that always returns fixed size (there may well be a better way to do this):
>>> def hex3(n):
... return "0x%s"%("00000000%s"%(hex(n&0xffffffff)[2:-1]))[-8:]
...
>>> print hex3(-1)
0xffffffff
>>> print hex3(17)
0x00000011
Or, avoiding the hex() altogether, thanks to Ignacio and bobince:
def hex2(n):
return "0x%x"%(n&0xffffffff)
def hex3(n):
return "0x%s"%("00000000%x"%(n&0xffffffff))[-8:]
Try this function:
'%#4x' % (-1 & 0xffffffff)
My company has hundreds, perhaps thousands, of test scripts written in Python. Most were written in Python 2, but they are slowly being converted to Python 3. I have found several of them that use hexadecimal literals to represent negative numbers that are to be stored in numpy int8 objects. This was OK in Python 2, where hex literals were assumed to be signed, but breaks in Python 3, where they're assumed to be unsigned.
x = int8(0xFF)
print x
prints -1 in Python 2, but in Python 3, it throws an overflow error.
So, I would like a Python script that reads through a Python script, identifies all strings beginning with "0x", and converts them to signed decimal integers. Does such a thing exist?
If you want to convert it to 64-bit signed integer then you can still use struct and pack it as unsigned integer ('Q'), then unpack as signed ('q'):
>>> struct.unpack('<q', struct.pack('<Q', int('0xb69958096aff3148', 16)))
(-5289099489896877752,)
I would recommend the bitstring package available through conda or pip.
from bitstring import BitArray
b = BitArray('0xb69958096aff3148')
b.int
# returns
-5289099489896877752
Want the unsigned int?:
b.uint
# returns:
13157644583812673864
I can't for the life of me figure out how i can input a normal base10 number and convert it into a Hex Signed 2's complement. Basically I just want to be able to get the result that this website would give me.
Thanks in advance
Python converts FFFF at 'face value', to decimal 65535
input = 'FFFF'
val = int(input,16) # is 65535
You want it interpreted as a 16-bit signed number. The code below will take the lower 16 bits of any number, and 'sign-extend', i.e. interpret as a 16-bit signed value and deliver the corresponding integer
val16 = ((val+0x8000)&0xFFFF) - 0x8000
This is easily generalized
def sxtn( x, bits ):
h= 1<<(bits-1)
m = (1<<bits)-1
return ((x+h) & m)-h
In a language like C, 'FFFF' can be interpreted as either a signed (-1) or unsigned (65535) value. You can use Python's struct module to force the interpretation that you're wanting.
Note that there may be endianness issues that the code below makes no attempt to deal with, and it doesn't handle data that's more than 16-bits long, so you'll need to adapt if either of those cases are in effect for you.
import struct
input = 'FFFF'
# first, convert to an integer. Python's going to treat it as an unsigned value.
unsignedVal = int(input, 16)
assert(65535 == unsignedVal)
# pack that value into a format that the struct module can work with, as an
# unsigned short integer
packed = struct.pack('H', unsignedVal)
assert('\xff\xff' == packed)
# ..then UNpack it as a signed short integer
signedVal = struct.unpack('h', packed)[0]
assert(-1 == signedVal)
You can interpret the bytes as a two's complement signed integer using bitwise operations. For example, for a 16-bit number:
def s16(value):
return -(value & 0x8000) | (value & 0x7fff)
Therefore:
>>> s16(int('0xffd2', 16))
-46
>>> s16(int('0xffcb', 16))
-53
>>> s16(int('0xffcc', 16))
-52
>>> s16(int('0x10', 16))
16
>>> s16(int('0xd', 16))
13
>>> s16(int('0x0', 16))
0
>>> s16(int('0xfffe', 16))
-2
This can be extended to any bit-length string, by setting the masks so the first mask matches the most-significant bit (0x8000 == 1 << 15 == 0b1000000000000000) and the second mask matches the all the remaining bits (0x7fff == (1 << 15) - 1 == 0b0111111111111111).
Once you have the unsigned value, it's very easy to convert to signed.
if value >= 0x8000:
value -= 0x10000
This is for a 16-bit number. For a 32-bit number just add 4 zeros to each of the magic constants. Those constants can also be calculated as 1 << (bits - 1) and 1 << bits.
The main problem is that int() cannot know how long the input is supposed to be; or in other words: it cannot know which bit is the MSB (most significant bit) designating the sign. In python, int just means "an integer, i.e. any whole number". There is no defined bit size of numbers, unlike in C.
For int(), the inputs 000000bd and bd therefore are the same; and the sign is determined by the presence or absence of a - prefix.
For arbitrary bit count of your input numbers (not only the standard 8, 16, 32, ...), you will need to do the two-complement conversion step manually, and tell it the supposed input size. (In C, you would do that implicitely by assigning the conversion result to an integer variable of the target bit size).
def hex_to_signed_number(s, width_in_bits):
n = int(s, 16) & (pow(2, width_in_bits) - 1)
if( n >= pow(2, width_in_bits-1) ):
n -= pow(2, width_in_bits)
return n
Some testcases for that function:
In [6]: hex_to_signed_number("bd", 8)
Out[6]: -67
In [7]: hex_to_signed_number("bd", 16)
Out[7]: 189
In [8]: hex_to_signed_number("80bd", 16)
Out[8]: -32579
In [9]: hex_to_signed_number("7fff", 16)
Out[9]: 32767
In [10]: hex_to_signed_number("8000", 16)
Out[10]: -32768
print(int.from_bytes(bytes.fromhex("bd"), byteorder="big", signed=True))
You can convert the string into Bytes and then convert bytes to int by adding signed to True which will give you negative integer value.