Python中数字的二进制补码

12

我正在编写代码,其中包含带有符号位(即二进制补码)的正负16位长的数字。这意味着我能够表示的最小数字是-32768,在二进制补码形式下为1000 0000 0000 0000。我能够表示的最大数字是32767,在二进制补码形式下为0111 1111 1111 1111

我的问题在于Python将负数与正数使用相同的二进制表示方式表示,只是在前面加上负号,例如-16384显示为-0100 0000 0000 0000,但我想要显示的是1100 0000 0000 0000这样的数值。

我不太确定如何编写代码来实现这一点。这是我的代码。如果数字介于180和359之间,则它将是负数。我需要将其显示为二进制补码值,但我不知道如何实现。

def calculatebearingActive(i):

    numTracks = trackQty_Active
    bearing = (((i)*360.0)/numTracks)
    if 0< bearing <=179:
        FC = (bearing/360.0)
        FC_scaled = FC/(2**(-16))
        return int(FC_scaled)

    elif 180<= bearing <=359:
        FC = -1*(360-bearing)/(360.0)
        FC_scaled = FC/(2**(-16))
        return int(FC_scaled)

    elif bearing ==360:
        FC = 0
        return FC

1
你只关心显示还是也需要进行计算? - Ayman
如果没有看到您的代码,很难找到您的错误。 - user2357112
4
为什么您要坚持将“complement”编辑成“compliment”? - zhangxaochen
为什么不使用实际的16位数字类型?Numpy有这些类型。 - Veedrac
6个回答

13

如果你正在进行类似于以下的操作

format(num, '016b')

要将数字转换为二进制补码字符串表示形式,您需要在将其转换为字符串之前对负数进行二进制补码运算:

format(num if num >= 0 else (1 << 16) + num, '016b')

或者将其模数为65536
format(num % (1 << 16), '016b')

1
format(num if num >= 0 else (1 << 16) - num, '016b')num=-16384 的情况下得到的是 '10100000000000000',然而 OP 想要的是 '1100 0000 0000 0000' - zhangxaochen
@zhangxaochen:哎呀,那个“-”需要改成“+”。已经修复了。 - user2357112
这正是我想要的。非常感谢。 - user3299406
3
您也可以使用位掩码来执行 format(num & 0xffff, '016b') - dcoles

2

一个值的二进制补码等于其一的补码加上一。

你可以根据此编写自己的转换函数:

def to_binary(value):
    result = ''
    if value < 0:
        result = '-'
        value = ~value + 1
    result += bin(value)
    return result

结果如下所示:
>>> to_binary(10)
'0b1010'
>>> to_binary(-10)
'-0b1010'

编辑:要显示没有负号的位,您可以使用此函数:

def to_twoscomplement(bits, value):
    if value < 0:
        value = ( 1<<bits ) + value
    formatstring = '{:0%ib}' % bits
    return formatstring.format(value)

>>> to_twoscomplement(16, 3)
'0000000000000011'
>>> to_twoscomplement(16, -3)
'1111111111111101'

0
Python可以保存无限的整数值,位表示将适应任何你输入的数字。因此,在这个上下文中,二进制补码等技术细节是没有意义的。在C语言中,'b1111111111111111'表示int16为-1,uint16或int32为65535。而在Python中,它始终是65535,因为int类型会适应保存这样的值。
我认为这就是为什么他们选择在负数前面加上“-”,而不管字符串表示形式是二进制、八进制、十六进制还是十进制。
如果你希望复制C语言的行为,并以二进制补码形式表示负数,你有以下几个选项:

1. 使用numpy进行int > uint > bin转换

最直接的方法是将值转换为有符号的有限整数,然后将其作为无符号整数读取。
如果你可以使用numpy,代码非常容易阅读:
>>> bin(np.int16(-30).astype(np.uint16))
'0b1111111111100010'
>>> bin(np.int16(-1).astype(np.uint16))
'0b1111111111111111'
>>> bin(np.int16(-2).astype(np.uint16))
'0b1111111111111110'
>>> bin(np.int16(-16).astype(np.uint16))
'0b1111111111110000'

使用结构体将2个int转为uint再转为二进制

你也可以使用结构体来完成类似的操作,但是这可能会稍微难理解一些。

>>> bin(struct.unpack('>H', struct.pack('>h', 30))[0])
'0b1111111111100010'
>>> bin(struct.unpack('>H', struct.pack('>h', -1))[0])
'0b1111111111111111'
>>> bin(struct.unpack('>H', struct.pack('>h', -2))[0])
'0b1111111111111110'
>>> bin(struct.unpack('>H', struct.pack('>h', -16))[0])
'0b1111111111110000'

注意:h是有符号的,H是无符号的16位整数,“>”代表大端序,如果你想直接读取字节而不将它们转换回整数,它非常方便。
3. 使用结构体进行int转换,然后逐字节读取并转换为二进制。
>>> ''.join(f'{byte:08b}' for byte in struct.pack('>h', -1<<15))
'1000000000000000'
>>> ''.join(f'{byte:08b}' for byte in struct.pack('>h', -1))
'1111111111111111'
>>> ''.join(f'{byte:08b}' for byte in struct.pack('>h', -2))
'1111111111111110'
>>> ''.join(f'{byte:08b}' for byte in struct.pack('>h', -16))
'1111111111110000'

请注意,这里有一些小问题,因为您需要记住使用“08”来强制字节二进制表示为8位数字。

3 直接从数学上讲

最后,您可以查看维基百科关于二进制补码表示的内容,并直接从数学公式二进制补码 > 从2N中减去中实现它。

>>> bin(2**16 -16)
'0b1111111111110000'
>>> bin(2**16 -3)
'0b1111111111111101'

这个看起来超级简单,但如果你不熟悉二进制补码表示的工作方式,就很难理解。


0

如果您想使用16位存储数字,可以使用结构体。

import struct

>>> struct.pack('h', 32767)
'\xff\x7f'
>>> struct.pack('h', -32767)
'\x01\x80'

您可以使用unpack进行解包操作

>>> a = struct.pack('h', 32767)
>>> struct.unpack('H', a)
32767

如果一个接口要解包,它会看到二进制补码数吗?我的意思是,它是否知道打包的值是负数? - user3299406

-1

以下是在Python中以二进制形式打印补码(1s和2s)时需要注意的一些事项:

UNSIGNED RANGE:      0 to (2^k)-1 for k bit number 
               ex:   0 to (2^32)-1 numbers
               ex:   0 to 7 for 3 bit unsigned numbers (count = 8)

SIGNED RANGE:       -2^(k-1) to +2^(k-1)-1 for 1+k bit number (k-1 is for dividing current range k into two equal half)
               ex:  -2^31 to +(2^31)-1 numbers  
               ex   -8 to +7 for 1+3 bit signed numbers (count = 8)


bin(int)->str converts an integer to binary string
   CAVEAT: 1. Since in python there is no limit to length of integer
           for ~x or !x (1s_complement/not/negate) we can't determine how many bits after MSB needs to be 1
           so python just prints out unsigned value of target negative number in binary format with a
           '-' sign in the beginning
           ex: for x = b'01010'(10) we get ~x = -0b'1011' (-11) 
               but we should be getting -16+5 = -11 
               (-b'10000'+b'00101') = -b'10101' (-11 signed) or (21 unsigned)
               to get real binary value after negation(or 1s complement) one could simulate it
               NOTE:  2^0 is always 1, so (2**0 == 1) in python
               NOTE:  (1 << k) is always 2^k (left shift is 2 raised to the power k)
               ex: bin((1 << k)-1 - x) which is ((2^k)-1)-x  (1s complement)
               ex: bin((1 << k)-1 - x) + 1 which is (2^k)-x  (2s complement)
          2. Same goes for reverse parsing of signed binary string to int: 
             ex: int("-0b'0101'", 2) gives -5 but instead it actually is -11 assuming -0b represents all bits 
                 from MSB till current to be like 1111......0101 which is actually -16+5 = -11
                 BUT due to PYTHON's limitation of representing signed binary values we need to adhere to 
                 current way of parsing considering unsigned binary strings with sign in front for -ve numbers

# NOTE: how the significant prefix zeros doesn't matter in both +ve and -ve cases

# Byte type inputs
x = b'+1010'            # valid +ve number byte string
x = b'1010'             # valid +ve number byte string
x = b'-1010'            # valid -ve number byte string
x = b'+01010'           # valid +ve number byte string
x = b'01010'            # valid +ve number byte string
x = b'-01010'           # valid -ve number byte string

int(b'101')             # interprets as base 10 for each digit
int(b'101', 2)          # interprets as base 2 for each digit
int(b'101', 8)          # interprets as base 8 for each digit
int(b'101', 10)         # interprets as base 10 for each digit
int(b'101', 16)         # interprets as base 16 for each digit

# String type inputs
x = '+1010'            # valid +ve number string
x = '1010'             # valid +ve number string
x = '-1010'            # valid -ve number string
x = '+01010'           # valid +ve number string
x = '01010'            # valid +ve number string
x = '-01010'           # valid -ve number string

int('101')             # interprets as base 10 for each digit
int('101', 2)          # interprets as base 2 for each digit
int('101', 8)          # interprets as base 8 for each digit
int('101', 10)         # interprets as base 10 for each digit
int('101', 16)         # interprets as base 16 for each digit

# print(bin(int(x, 2)), int(x,2), ~int(x, 2), bin(~int(x,2)), "-"+bin((1<<k)-1 - int(x,2)))


k = 5                   # no of bits
assert 2**0 == 1        # (2^0 is always 1)
_2k = (1 << k)          # (2^k == left shift (2^0 or 1) by k times == multiply 2 by k times)
x = '01010'             # valid +ve number string
x = int(x,2)
print("input:", x)      # supposed to be 1s complement of binStr but due to python's limitation, 
                        # we consider it -(unsigned binStr)
_1s = '-'+bin((_2k-1)-x)
print("1s complement(negate/not): ", _1s, ~x)
_2s = '-'+bin(_2k-x)
print("2s complement(1s +1):      ", _2s, ~x+1)

output:
k = 5 (5 bit representation)
input: 10
1s complement(negate/not):  -0b10101 -11
2s complement(1s +1):       -0b10110 -10

k=32 (32 bit representation)
input: 10
1s complement(negate/not):  -0b11111111111111111111111111110101 -11
2s complement(1s +1):       -0b11111111111111111111111111110110 -10

-1

由于您没有提供任何代码示例,我无法确定发生了什么。根据您示例中的数字,我认为您没有使用bin(yourint),因为您的输出不包含0b。也许您已经在示例中切掉了它。

如果您将二进制数据存储为字符串,可以尝试以下方法:

    def handle_negatives(binary_string):
        If binary_string < 0:
            binary_string = '1' + str(binary_string)[1:]
        Return binary_string

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