CPython integer类型将符号存储在结构的特定字段中。当执行按位运算时,CPython用两个补码替换负数,有时(!)执行相反的运算(即用负数替换两个补码)。在

按位运算

整数的内部表示是PyLongObject结构,它包含PyVarObject结构。(当CPython创建一个新的PyLong对象时,它为结构分配内存,并为数字分配一个尾随空格。)这里重要的是PyLong的大小是:PyVarObject嵌入结构的ob_size字段包含整数的大小(以数字为单位)(数字可以是15位或30位数字)。

如果整数为负数,则该大小为减去位数。在

如您所见,内部CPython对整数的表示方式与通常的二进制表示方式相差甚远。然而CPython必须为各种目的提供位操作。让我们看看the code中的注释:static PyObject *

long_bitwise(PyLongObject *a,
char op, /* '&', '|', '^' */
PyLongObject *b)
{
/* Bitwise operations for negative numbers operate as though
on a two's complement representation. So convert arguments
from sign-magnitude to two's complement, and convert the
result back to sign-magnitude at the end. */
/* If a is negative, replace it by its two's complement. */
/* Same for b. */
/* Complement result if negative. */
}
为了在按位运算中处理负整数,CPython使用2的补码(实际上,这是一个2的补码,但我不详细讨论)。但请注意“符号规则”(名字是我的):结果的符号是应用于数字符号的位运算符。更准确地说,如果nega negb == 1,negx=1表示阴性,0表示阳性)。Simplified code:
^{pr2}$
二进制格式
另一方面,格式化程序不执行二者的补码,即使是在二进制表示中:[format_long_internal](https://github.com/python/cpython/blob/master/Python/formatter_unicode.c#L839)调用[long_format_binary](https://github.com/python/cpython/blob/master/Objects/longobject.c#L1934)并删除两个前导字符,但保留符号。见the code:/* Is a sign character present in the output? If so, remember it
and skip it */
if (PyUnicode_READ_CHAR(tmp, inumeric_chars) == '-') {
sign_char = '-';
++prefix;
++leading_chars_to_skip;
}
long_format_binary函数不执行任何2的补码:只输出以2为基数的数字,preceeded by the sign。在if (negative) \
* p = '-'; \
你的问题
我会按照你的回复顺序:>>> x = -4
>>> print("{} {:b}".format(x, x))
-4 -100
没什么奇怪的,因为格式中没有二的补码,只有一个符号。在>>> mask = 0xFFFFFFFF
>>> print("{} {:b}".format(x & mask, x & mask))
4294967292 11111111111111111111111111111100

数字-4为负数。因此,它被逻辑“与”之前的“二”补码替换,一位数接一位数。你本以为结果会变成负数,但请记住“符号规则”:

>>> nega=1; negb=0
>>> nega & negb
0

因此:1。结果没有负号。结果不是两个结果的补充。你的结果符合“符号规则”,即使这个规则看起来不是很直观。在

现在,最后一部分:

>>> x = 0b11111111111111111111111111111100
>>> print("{} {:b}".format(x, x))
4294967292 11111111111111111111111111111100
>>> print("{} {:b}".format(~(x ^ mask), ~(x ^ mask)))
-4 -100
同样,-4是负的,因此被它的两个补码0b11111111111111111111111111111100替换,然后用0b11111111111111111111111111111111进行异或。结果是0b11(3)。你取一元补码,也就是0b11111111111111111111111111111100,但这次符号是负数:>>> nega=1; negb=0
>>> nega ^ negb
1

因此,正如您所期望的,结果是补的并得到负号。在

结论:我想没有一个完美的解决方案可以让任意长符号数和提供按位操作,但文档中并没有对所做选择进行详细说明。在