Python中的十六进制数的二进制补码

9

以下是a和b(十六进制),表示的是二进制补码有符号数字。例如:

a = 0x17c7cc6e
b = 0xc158a854

现在我想知道十进制下a和b的有符号表示方式。抱歉,我是一个低级程序员并且是python新手;为此感到很愚蠢。我不在乎额外的库,但答案应该简单明了。背景:a和b是从UDP数据包中提取的数据。我无法控制格式。因此,请不要给我一个假设我可以提前更改这些变量格式的答案。
我用以下内容将a和b转换为:
aBinary = bin(int(a, 16))[2:].zfill(32) => 00010111110001111100110001101110 => 398969966
bBinary = bin(int(b, 16))[2:].zfill(32) => 11000001010110001010100001010100 => -1051154348

我尝试做类似于这样的事情(不起作用):
if aBinary[1:2] == 1:
aBinary = ~aBinary + int(1, 2)

在Python中,如何正确地完成这项任务?

7个回答

10

为什么不使用 ctypes

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

7

在Python中实现此操作的好方法是使用位运算。例如,对于32位值:

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

将此应用于您的价值观:
>>> s32(a)
398969966
>>> s32(b)
-1051154348

这个函数的作用是扩展符号位,这样就可以正确地解释带有正确符号和数值的值。
Python的处理方式有点棘手,因为它使用任意精度整数,所以负数会被视为具有无限前导1位的数。例如:
>>> bin(-42 & 0xff)
'0b11010110'
>>> bin(-42 & 0xffff)
'0b1111111111010110'
>>> bin(-42 & 0xffffffff)
'0b11111111111111111111111111010110'

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

我收到了“OverflowError:Python int太大,无法转换为C long”的错误。 - betontalpfa

4

您至少需要知道数据的宽度。例如,0xc158a854 有8位十六进制数字,因此它必须至少为32位宽;它似乎是一个无符号32位值。我们可以使用一些位运算来处理它:

In [232]: b = 0xc158a854

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

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

L 这里标志着 Python 2 已经切换为将值视为 long 类型进行处理。通常情况下这并不重要,但在这种情况下表明我已经在该安装中使用超出常见 int 范围的值。用于从二进制结构(如 UDP 数据包)中提取数据的工具是 struct.unpack;如果你一开始就告诉它你的值是有符号的,那么它会生成正确的值:

In [240]: s = '\xc1\x58\xa8\x54'

In [241]: import struct

In [242]: struct.unpack('>i', s)
Out[242]: (-1051154348,)

假设使用的是二进制补码表示法;而一的补码(例如UDP中使用的校验和)、符号加数或IEEE 754浮点数则是一些不太常见的数字编码方式。

1
谢谢!第一种方法非常好,是一个没有库的单行代码,+1。 - Nimjox

1
另一个现代化的解决方案:
>>> h = 0xc158a854
>>> int.from_bytes(bytes.fromhex(hex(h)[2:]), byteorder='big', signed=True)
-1051154348

0
value=input("enter hexa decimal value for getting compliment values:=")
highest_value="F"*len(value)
resulting_decimal=int(highest_value,16)-int(value,16)
ones_compliment=hex(resulting_decimal)
twos_compliment=hex(r+1)
print(f'ones compliment={ones_compliment}\n twos complimet={twos_compliment}')

0

2^31 = 0x80000000(符号位,在二进制补码中表示-2^31)
2^31-1 = 0x7fffffff(所有正数位)

因此,(n&0x7fffffff)-(n&0x80000000)将正确应用符号

你甚至可以做,n - ((n&0x80000000)<<1) 来两次减去最高位的值

最后还有一种方法,(n&0x7fffffff) | -(n&0x80000000) 只是合并负数位,而非相减

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接