Python中的两个补码二进制?

时间:2012-10-18 02:15:00

标签: python binary bit-manipulation

Python中的整数存储在两个补码中,对吗?

虽然:

>>> x = 5
>>> bin(x)
0b101

>>> x = -5
>>> bin(x)
-0b101

那太蹩脚了。我怎么让python给我真正的二进制位数,而没有它的前面的0b?所以:

>>> x = 5
>>> bin(x)
0101
>>> y = -5
>>> bin(y)
1011

15 个答案:

答案 0 :(得分:62)

如果您提供面具,效果最佳。这样就可以指定签名扩展的范围。

>>> bin(-27 & 0b1111111111111111)
'0b1111111111100101'

或者更一般地说:

def bindigits(n, bits):
    s = bin(n & int("1"*bits, 2))[2:]
    return ("{0:0>%s}" % (bits)).format(s)

>>> print bindigits(-31337, 24)
111111111000010110010111

在基本理论中,数字的实际宽度是存储大小的函数。如果它是一个32位数字,那么负数在一组32的MSB中有1。如果它是64位值,那么有64位要显示。

但在Python中,整数精度仅限于硬件的约束。在我的计算机上,这个实际上正常工作,但它只消耗9GB的RAM来存储 x 的值。任何更高的东西,我得到一个MemoryError。如果我有更多的RAM,我可以存储更大的数字。

>>> x = 1 << (1 << 36)

那么考虑到这一点,二进制数代表-1?正如前面的例子所示,Python能够很好地解释数百万(甚至数十亿)的精度。在2的补码中,符号位一直向左扩展,但在Python中没有预定义的位数;你有多少需要。

但是你会遇到歧义:二元1代表1还是-1?好吧,也可能是。 111代表7还是-1?同样,它也可能是。 111111111代表511-1 ......嗯,这两者都取决于您的精确度。

Python需要一种以二进制形式表示这些数字的方法,以便它们的含义没有歧义。 0b前缀只是说“这个数字是二进制的”。就像0x表示“这个数字是十六进制”一样。所以,如果我说0b1111,我怎么知道用户是想要-1还是15?有两种选择:

选项A: 符号位
您可以声明所有数字都是有符号的,最左边的位是符号位。这意味着0b1为-1,而0b01为1.这也意味着0b111也是-1,而0b0111是7.最后,这可能是更有说服力而不是有用,特别是因为大多数二进制算术无论如何都是无符号的,并且人们更容易因错误地将数字标记为否定而导致错误,因为它们不包含显式符号位。

选项B: 符号指示
使用此选项,二进制数字表示无符号,负数字表示“ - ”前缀,就像它们在十进制中一样。这是(a)与十进制更一致,(b)与最有可能使用二进制值的方式更兼容。您无法使用其二进制补码表示来指定负数,但请记住,两个补码是存储实现详细信息,而不是对基础值本身的正确指示。它不应该是用户必须理解的东西。

最后,选项B最有意义。混淆较少,用户无需了解存储详细信息。

答案 1 :(得分:13)

要将二进制序列正确解释为二进制补码,需要与序列关联的长度。在处理与CPU寄存器直接对应的低级类型时,存在隐式长度。由于Python整数可以具有任意长度,因此实际上没有内部二进制补码格式。由于没有与数字相关的长度,因此无法区分正数和负数。为了消除歧义,bin()在格式化负数时包含减号。

Python的任意长度整数类型实际上使用符号幅度内部格式。逻辑运算(位移,和/或等)旨在模仿二进制补码格式。这是多个精度库的典型特征。

答案 2 :(得分:3)

不确定如何使用标准库获取所需内容。有一些脚本和包可以为你进行转换。

我只想注意“为什么”,以及为什么它不蹩脚。

bin()不返回二进制位。它将数字转换为二进制字符串。根据python语言定义,前导'0b'告诉解释器你正在处理二进制数。这样你就可以直接使用二进制数,比如这个

>>> 0b01
1
>>> 0b10
2
>>> 0b11
3
>>> 0b01 + 0b10
3
那不是蹩脚的。那很好。


http://docs.python.org/library/functions.html#bin

  

箱(x)的

     
    

将整数转换为二进制字符串。

  

http://docs.python.org/reference/lexical_analysis.html#integers

  

整数和长整数文字由以下词汇定义描述:

     
    

bininteger :: =“0”(“b”|“B”)bindigit +

         

bindigit :: =“0”| “1”

  

答案 3 :(得分:3)

tobin = lambda x, count=8: "".join(map(lambda y:str((x>>y)&1), range(count-1, -1, -1)))

e.g。

tobin(5)      # =>  '00000101'
tobin(5, 4)   # =>      '0101'
tobin(-5, 4)  # =>      '1011'

或者作为明确的功能:

# Returns bit y of x (10 base).  i.e. 
# bit 2 of 5 is 1
# bit 1 of 5 is 0
# bit 0 of 5 is 1
def getBit(y, x):
    return str((x>>y)&1)

# Returns the first `count` bits of base 10 integer `x`
def tobin(x, count=8):
    shift = range(count-1, -1, -1)
    bits = map(lambda y: getBit(y, x), shift)
    return "".join(bits)

(改编自W.J. Van de Laan's评论)

答案 4 :(得分:2)

我不完全确定您最终想要做什么,但您可能希望查看bitarray包。

答案 5 :(得分:1)

对于正数,只需使用:

bin(x)[2:].zfill(4)

对于负数,它有点不同:

bin((eval("0b"+str(int(bin(x)[3:].zfill(4).replace("0","2").replace("1","0").replace("2","1"))))+eval("0b1")))[2:].zfill(4)

作为一个完整的脚本,它应该是这样的:

def binary(number):
    if number < 0:
        return bin((eval("0b"+str(int(bin(number)[3:].zfill(4).replace("0","2").replace("1","0").replace("2","1"))))+eval("0b1")))[2:].zfill(4)
    return bin(number)[2:].zfill(4)      
x=input()
print binary(x)

答案 6 :(得分:1)

def tobin(data, width):
    data_str = bin(data & (2**width-1))[2:].zfill(width)
    return data_str

答案 7 :(得分:1)

使用切片去掉不需要的'0b'。

  
    
      

bin(5)[2:]       '101'

    
  

或者如果你想要数字,

  
    
      

tuple ( bin(5)[2:] )       ('1','0','1')

    
  

甚至

  
    
      

map( int, tuple( bin(5)[2:] ) )       [1,0,1]

    
  

答案 8 :(得分:1)

修改tylerl非常有用的答案,为正数和负数提供符号扩展(无错误检查)。

def to2sCompStr(num, bitWidth):
    num &= (2 << bitWidth-1)-1 # mask
    formatStr = '{:0'+str(bitWidth)+'b}'
    ret =  formatStr.format(int(num))
    return ret

示例:

In [11]: to2sCompStr(-24, 18)
Out[11]: '111111111111101000'

In [12]: to2sCompStr(24, 18)
Out[12]: '000000000000011000'

答案 9 :(得分:1)

没必要,已经是。只是python选择以不同的方式表示它。如果你开始分别打印每个半字节,它将显示其真实的颜色。

IDialogWaterfallStep

输出很简单:

checkNIB = '{0:04b}'.format
checkBYT = lambda x: '-'.join( map( checkNIB, [ (x>>4)&0xf, x&0xf] ) ) 
checkBTS = lambda x: '-'.join( [ checkBYT( ( x>>(shift*8) )&0xff ) for shift in reversed( range(4) ) if ( x>>(shift*8) )&0xff ] )


print( checkBTS(-0x0002) )

现在,当你想显示一个半字节的二进制补码时,它会恢复原始表示形式,但是如果将它分成半部分,那么它仍然是可能的。 请记住,最好的结果是使用负十六进制和二进制整数解释简单数字,而不是十六进制,您可以设置字节大小。

答案 10 :(得分:1)

一个减号的意思是 mod value减去正值。 因此,我认为,补充-27的简单方法是

bin((1<<32) - 27)  // 32 bit length  '0b11111111111111111111111111100101'
bin((1<<16) - 27)
bin((1<<8) - 27)   // 8 bit length  '0b11100101'

答案 11 :(得分:1)

您可以使用 Binary fractions 包。这个包用二进制整数和二进制分数实现 >>> from binary_fractions import TwosComplement >>> TwosComplement.to_float("11111111111") # TwosComplement --> float -1.0 >>> TwosComplement.to_float("11111111100") # TwosComplement --> float -4.0 >>> TwosComplement(-1.5) # float --> TwosComplement '10.1' >>> TwosComplement(1.5) # float --> TwosComplement '01.1' >>> TwosComplement(5) # int --> TwosComplement '0101' 。您可以将二进制分数字符串转换为二进制补码,反之亦然

示例:

Binary

要将它与二进制而不是浮点一起使用,您可以在同一个包中使用 # no. of rows to read per chunk factor = 10**7 # gather files to combine file_lst = [] for fl in os.listdir('output/'): if not fl.startswith('combined'): file_lst.append(fl) # combined file name with tb.File('output/combined.h5', 'w') as file_cmb: for file_idx, filename in enumerate(file_lst): if file_idx == 0: # copy file-1 dataset to new file with tb.File(f'output/{filename}', 'r') as file1: z = file1.copy_node('/', name='dataset_1', newparent=file_cmb.root, newname='dataset_1') print(f'File1-{filename} shape: {file1.root.dataset_1.shape[0]}') else: with tb.File(f'output/{filename}', 'r') as file2: file2_dset = file2.root.dataset_1 shape = file2_dset.shape[0] print(f'File2-{filename} shape: {shape}') chunk_loops = shape//factor if shape > chunk_loops*factor: chunk_loops += 1 chunk_start, chunk_end = 0, 0 for alpha in range(chunk_loops): if chunk_start + factor > shape: chunk_end = shape else: chunk_end = chunk_start + factor z.append(file2_dset[chunk_start:chunk_end]) chunk_start = chunk_end print(f'Combined file shape: {z.shape}') 类。

PS:无耻的插件,我是这个包的作者。

答案 12 :(得分:0)

这是Tylerl answer的一个更具可读性的版本,例如,假设你想要 8位的负面表示 -2 补充“:

bin(-2 & (2**8-1))

2 ** 8代表第九位(256),减去1,你将所有前面的位设置为一(255)

对于8位和16位掩码,可以用0xff或0xffff替换(2 ** 8-1)。在该点之后,十六进制版本变得不那么readalbe。

如果不清楚,这是它的常规功能:

def twosComplement (value, bitLength) :
    return bin(value & (2**bitLength - 1))

答案 13 :(得分:0)

我们可以利用按位异或的特性。使用按位 XOR 翻转位,然后加 1。然后您可以使用 python 内置的 bin() 函数来获取 2 的补码的二进制表示。这是一个示例函数:

def twos_complement(input_number):
    print(bin(input_number))                            # prints binary value of input
    mask = 2**(1 + len(bin(input_number)[2:])) - 1      # Calculate mask to do bitwise XOR operation
    twos_comp = (input_number ^ mask) + 1               # calculate 2's complement, for negative of input_number (-1 * input_number)
    print(bin(twos_comp))                               # print 2's complement representation of negative of input_number.   

答案 14 :(得分:-1)

我希望这可以解决您的问题`

num = input("Enter number : ")
bin_num=bin(num)
binary = '0' + binary_num[2:]
print binary