foosion
foosion

Reputation: 7918

Hex string to signed int in Python

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

Upvotes: 25

Views: 65503

Answers (6)

Joe Marley
Joe Marley

Reputation: 309

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)

Upvotes: 1

jiasli
jiasli

Reputation: 9148

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

Upvotes: 2

Mark Tolonen
Mark Tolonen

Reputation: 178179

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

Upvotes: 53

amarchiori
amarchiori

Reputation: 1389

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)

Upvotes: 4

kichik
kichik

Reputation: 34744

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

Upvotes: 17

justsomedude
justsomedude

Reputation: 41

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)

Upvotes: 4

Related Questions