在 python 中对二进制运算中的整数求反的好方法是什么?
What is good way to negate an integer in binary operation in python?
根据我读到的 the binary representation of integers,第一位是符号(正或负)。
假设我们有一个整数 x = 5
和 sys.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位数字的个数。
根据我读到的 the binary representation of integers,第一位是符号(正或负)。
假设我们有一个整数 x = 5
和 sys.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位数字的个数。