26

How do I convert a hex string to a signed int in Python 3?

The best I can come up with is

h = '9DA92DAB'
b = bytes(h, 'utf-8')
ba = binascii.a2b_hex(b)
print(int.from_bytes(ba, byteorder='big', signed=True))

Is there a simpler way? Unsigned is so much easier: int(h, 16)

BTW, the origin of the question is itunes persistent id - music library xml version and iTunes hex version

1
  • The two lines b= and ba= can be replaced with ba=bytes.fromhex(h). See Lennart's comment below. Commented Jul 18, 2011 at 11:21

6 Answers 6

53

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
Sign up to request clarification or add additional context in comments.

5 Comments

bits = hexstr.__len__() so it is not necessary to pass bits in as a parameter. Unless, for example, you want to have hexstr = 'FF' represent a 32 bit value.
@DeanM I assume you really mean len(hexstr) * 4, and yes the number could be missing leading zeros so I was being explicit about the number of bits in the answer.
Alternative int.from_bytes(bytearray([0xFF,0xFE]), byteorder='big',signed=True) (first example)
@pas-calc that doesn’t start with a hex string as OP requested
@MarkTolonen yes you're right. A way fully using python built in methods would be first reading the string to a bytearray and then doing the arithmetic interpretation of MSB/LSB & signed, like: hex_string="FFFE"; ba=bytearray.fromhex(hex_string); val=int.from_bytes((ba), byteorder='big',signed=True); val
17
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'))

Comments

5

Converting string to bytes can be done with bytes.fromhex:

So it can be written as:

print(int.from_bytes(bytes.fromhex('9DA92DAB'), signed=True))

Output:

-1649857109

Comments

4

Here's a general function you can use for hex of any size:

import math

# hex string to signed integer
def htosi(val):
    uintval = int(val,16)
    bits = 4 * (len(val) - 2)
    if uintval >= math.pow(2,bits-1):
        uintval = int(0 - (math.pow(2,bits) - uintval))
    return uintval

And to use it:

h = str(hex(-5))
h2 = str(hex(-13589))
x = htosi(h)
x2 = htosi(h2)

Comments

4

This works for 16 bit signed ints, you can extend for 32 bit ints. It uses the basic definition of 2's complement signed numbers. Also note xor with 1 is the same as a binary negate.

# convert to unsigned
x = int('ffbf', 16) # example (-65)
# check sign bit
if (x & 0x8000) == 0x8000:
    # if set, invert and add one to get the negative value, then add the negative sign
    x = -( (x ^ 0xffff) + 1)

Comments

1

Here's a function to do the above. This will extend for whatever length you provide.

def hex_to_signed(source):
    """Convert a string hex value to a signed hexidecimal value.

    This assumes that source is the proper length, and the sign bit
    is the first bit in the first byte of the correct length.

    hex_to_signed("F") should return -1.
    hex_to_signed("0F") should return 15.
    """
    if not isinstance(source, str):
        raise ValueError("string type required")
    if 0 == len(source):
        raise valueError("string is empty")
    sign_bit_mask = 1 << (len(source)*4-1)
    other_bits_mask = sign_bit_mask - 1
    value = int(source, 16)
    return -(value & sign_bit_mask) | (value & other_bits_mask)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.