在 python 中对二进制运算中的整数求反的好方法是什么?

What is good way to negate an integer in binary operation in python?

根据我读到的 the binary representation of integers,第一位是符号(正或负)。

假设我们有一个整数 x = 5sys.getsizeof(x) returns 28(即 28 位的二进制表示)。

目前,我正在尝试使用 x|=(1<<27) 将第一位翻转为 1,但它 returns 134217733.

我只是想知道它是否需要是一些负数? (不是-5)

我这样做有什么问题吗?

python中的负数表示:

根据你想要多少个二进制数字,从一个数字中减去(2n):

>>> bin((1 << 8) - 1)
'0b11111111'
>>> bin((1 << 16) - 1)
'0b1111111111111111'
>>> bin((1 << 32) - 1)
'0b11111111111111111111111111111111'

生成补码函数(负数):

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'

参考:two's complement of numbers in python

您不能像您尝试的那样将 Python int 从正面切换到负面,只需稍微翻转其表示即可。您假设它存储在固定长度的二进制补码表示中。但是Python3中的整数不是固定长度的位串,也不是以二进制补码的形式存储的。相反,它们存储为 30 位或 15 位 "digits" 的可变长度字符串,符号单独存储(如 signed-magnitude representation)。因此 "lowest-level" 否定 Python int 的方法不是使用位操作,而是使用一元 - 运算符,它将切换其符号。 (有关 Python 3 来源的详细信息,请参阅此答案的末尾。)

(我还应该提到 sys.getsizeof() 而不是 告诉你 int 中的位数。它给你的字节数整数对象正在使用的内存。这也不是实际存储数字的字节数;这些字节中的大部分用于其他用途。)


您仍然可以在 Python 中使用二进制补码表示,方法是使用正数 int 模拟固定长度的位串。首先,选择您想要的长度,例如 6 位。 (您可以轻松地选择更大的数字,例如 28 或 594。)我们可以定义一些有用的常量和函数:

BIT_LEN = 6
NUM_INTS = 1 << BIT_LEN         # 0b1000000
BIT_MASK = NUM_INTS - 1         #  0b111111
HIGH_BIT = 1 << (BIT_LEN - 1)   #  0b100000

def to2c(num):
    """Returns the two's complement representation for a signed integer."""
    return num & BIT_MASK

def from2c(bits):
    """Returns the signed integer for a two's complement representation."""
    bits &= BIT_MASK
    if bits & HIGH_BIT:
        return bits - NUM_INTS

现在我们可以像您一样做一些事情了:

>>> x = to2c(2)
>>> x |= 1 << 5
>>> bin(x)
'0b100010'
>>> from2c(x)
-30

这表明将6位二进制补码表示中的数字2的高位打开会将数字变为-30。这是有道理的,因为 26-1 = 32,所以这个表示中的最小整数是 -32。并且 -32 + 2 = -30.


如果你对Python3如何存储整数的细节感兴趣,你可以查看Objects/longobject.c in the source. In particular, looking at the function _PyLong_Negate():

/* If a freshly-allocated int is already shared, it must
   be a small integer, so negating it must go to PyLong_FromLong */
Py_LOCAL_INLINE(void)
_PyLong_Negate(PyLongObject **x_p)
{
    PyLongObject *x;

    x = (PyLongObject *)*x_p;
    if (Py_REFCNT(x) == 1) {
        Py_SIZE(x) = -Py_SIZE(x);
        return;
    }

    *x_p = (PyLongObject *)PyLong_FromLong(-MEDIUM_VALUE(x));
    Py_DECREF(x);
}

你可以看到它在正常情况下所做的只是否定整数对象的 Py_SIZE() 值。 Py_SIZE() 只是对整数对象的 ob_size 字段的引用。当此值为0时,整数为0。否则,其符号为整数的符号,其绝对值为保存整数绝对值的数组中30位或15位数字的个数。