Two's complement Hex numbers in Python

Below are 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 and b in base 10. Sorry, I am a low level programmer and new to python; feel very silly asking about it. I don't need an additional library, but the answer should be simple and straight forward. Prerequisites: a and b are the extracted data from the UDP packet. I have no control over the format. So please don't give me an answer that would suggest that I can change the format of these modified options before distribution.

I converted a and b to the following:

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

      

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

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

      

What is the correct way to do this in python?

+3


source to share


4 answers


You need to know at least the width of your data. For example, 0xc158a854 has 8 hexadecimal digits, so the width must be at least 32 bits; it is an unsigned 32-digit value. We can handle it with some bitwise operations:

In [232]: b = 0xc158a854

In [233]: if b >= 1<<31: b -= 1<<32

In [234]: b
Out[234]: -1051154348L

      

L notes here that Python 2 has switched to treating the value as a long; this is usually not important, but in this case indicates that I was working with values ​​outside the total int range for this setting. A tool for extracting data from binary structures like UDP packets, struct.unpack ; if you just say 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,)

      

This involves the presentation of two complements; one addition (such as the checksum used in UDP), sign and value, or IEEE 754 floating point are some of the less common encodings for numbers.

+3


source


>>> import numpy
>>> numpy.int32(0xc158a854)
-1051154348

      



+3


source


A good way to do this in Python is to use 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-expanding the value, so it is interpreted correctly with the correct icon and value.

Python is a little trickier 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'

      

+3


source


2 ^ 31 = 0x80000000 (sign bit, which is -2 ^ 31 in two compliments)
2 ^ 31-1 = 0x7fffffff (all positive bits)

and hence (n and 0x7fffffff) - (n and 0x80000000) will correctly apply the sign

you could even do, n - ((n and 0x80000000) <1) to subtract the msb value twice

or finally there is (n and 0x7fffffff) | - (n and 0x80000000) to just flush the negative bit rather than subtract

def signedHex(n): return (n & 0x7fffffff) | -(n & 0x80000000)

signedHex = lambda n: (n & 0x7fffffff) | -(n & 0x80000000)

      

0


source







All Articles