54

Python中的整数存储在二进制补码中,对吗?

虽然:

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

和:

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

这很蹩脚。如何让 python 给我 REAL 二进制位的数字,并且前面没有 0b?所以:

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

15 回答 15

90

如果您提供口罩,效果最好。这样您就可以指定签名扩展的距离。

>>> 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 最有意义。混淆较少,用户无需了解存储详细信息。

于 2012-10-18T02:31:11.940 回答
16

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

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

于 2012-10-18T04:53:39.790 回答
6

一个负数的补码是 模值减去正值。所以我认为,-27的恭维的简单方法是

bin((1<<32) - 27)  // 32 bit length  '0b11111111111111111111111111100101'
bin((1<<16) - 27)
bin((1<<8) - 27)   // 8 bit length  '0b11100101'
于 2019-11-24T10:53:02.757 回答
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+

二进制数字 ::= "0" | “1”

于 2012-10-18T02:34:57.783 回答
3
tobin = lambda x, count=8: "".join(map(lambda y:str((x>>y)&1), range(count-1, -1, -1)))

例如

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)

(改编自WJ Van de Laan 的评论)

于 2013-03-24T09:13:27.960 回答
3

这是Tylerl answer的可读性更高的版本,例如,假设您希望-2在其“二进制补码”的8 位负表示中:

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

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

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

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

def twosComplement (value, bitLength) :
    return bin(value & (2**bitLength - 1))
于 2017-09-09T13:08:26.213 回答
2

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

于 2012-10-18T02:20:33.330 回答
2
def tobin(data, width):
    data_str = bin(data & (2**width-1))[2:].zfill(width)
    return data_str
于 2013-04-18T04:06:53.543 回答
2

使用切片去除不需要的“0b”。

bin(5)[2:] '101'

或者如果你想要数字,

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

甚至

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

于 2014-01-10T09:27:30.040 回答
2

您可以使用二进制分数包。这个包TwosComplement用二进制整数和二进制分数实现。您可以将二进制分数字符串转换为其二进制补码,反之亦然

例子:

>>> 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's 而不是 float's 一起使用,您可以使用Binary同一个包中的类。

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

于 2021-07-16T22:02:49.467 回答
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)
于 2012-10-18T02:31:38.370 回答
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'
于 2015-04-02T07:14:05.273 回答
1

不需要,已经是了。只是 python 选择以不同的方式表示它。如果您开始单独打印每个半字节,它将显示其真实颜色。

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) )

输出很简单:

>>>1111-1111-1111-1111-1111-1111-1111-1110  

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

于 2018-05-03T19:36:54.347 回答
0

我们可以利用按位异或的特性。使用按位异或翻转位,然后加 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.   
于 2021-02-05T02:51:36.057 回答
-3

我希望这能解决你的问题`

num = input("Enter number : ")
bin_num=bin(num)
binary = '0' + binary_num[2:]
print binary
于 2017-04-12T16:40:01.257 回答