Two's complement of Hex number in Python

16,700

Solution 1

You'll have to know at least the width of your data. For instance, 0xc158a854 has 8 hexadecimal digits so it must be at least 32 bits wide; it appears to be an unsigned 32 bit value. We can process it using some bitwise operations:

In [232]: b = 0xc158a854
In [233]: if b >= 1<<31: b -= 1<<32
In [234]: b
Out[234]: -1051154348L

The L here marks that Python 2 has switched to processing the value as a long; it's usually not important, but in this case indicates that I've been working with values outside the common int range for this installation. The tool to extract data from binary structures such as UDP packets is struct.unpack; if you just tell it that your value is signed in the first place, it will produce the correct value:

In [240]: s = '\xc1\x58\xa8\x54'
In [241]: import struct
In [242]: struct.unpack('>i', s)
Out[242]: (-1051154348,)

That assumes two's complement representation; one's complement (such as the checksum used in UDP), sign and magnitude, or IEEE 754 floating point are some less common encodings for numbers.

Solution 2

why not using ctypes ?

>>> import ctypes
>>> a = 0x17c7cc6e
>>> ctypes.c_int32(a).value
398969966
>>> b = 0xc158a854
>>> ctypes.c_int32(b).value
-1051154348

Solution 3

A nice way to do this in Python is using bitwise operations. For example, for 32-bit values:

def s32(value):
    return -(value & 0x80000000) | (value & 0x7fffffff)

Applying this to your values:

>>> s32(a)
398969966
>>> s32(b)
-1051154348

What this function does is sign-extend the value so it's correctly interpreted with the right sign and value.

Python is a bit tricky in that it uses arbitrary precision integers, so negative numbers are treated as if there were an infinite series of leading 1 bits. For example:

>>> bin(-42 & 0xff)
'0b11010110'
>>> bin(-42 & 0xffff)
'0b1111111111010110'
>>> bin(-42 & 0xffffffff)
'0b11111111111111111111111111010110'

Solution 4

>>> import numpy
>>> numpy.int32(0xc158a854)
-1051154348
Share:
16,700

Related videos on Youtube

Nimjox
Author by

Nimjox

Majored in Electrical Engineering but currently work as a software engineer, so I have lots of questions. If you're here then thanks for your help!

Updated on June 18, 2022

Comments

  • Nimjox
    Nimjox 11 months

    Below a and b (hex), representing two's complement signed binary numbers. For example:

    a = 0x17c7cc6e
    b = 0xc158a854
    

    Now I want to know the signed representation of a & b in base 10. Sorry I'm a low level programmer and new to python; feel very stupid for asking this. I don't care about additional library's but the answer should be simple and straight forward. Background: a & b are extracted data from a UDP packet. I have no control over the format. So please don't give me an answer that would assume I can change the format of those varibles before hand.

    I have converted a & b into the following with this:

    aBinary = bin(int(a, 16))[2:].zfill(32) => 00010111110001111100110001101110 => 398969966
    bBinary = bin(int(b, 16))[2:].zfill(32) => 11000001010110001010100001010100 => -1051154348
    

    I was trying to do something like this (doesn't work):

    if aBinary[1:2] == 1:
    aBinary = ~aBinary + int(1, 2)
    

    What is the proper way to do this in python?

  • Nimjox
    Nimjox over 8 years
    Thanks! The first method works great and it's a one liner with no libraries +1 for that.
  • betontalpfa
    betontalpfa over 4 years
    I get OverflowError: Python int too large to convert to C long

Related