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

那年仲夏 提交于 2019-12-12 18:04:15

问题


Based on what I've read about the binary representation of integers, the first bit is for sign (positive or negative).

Let's say we have an integer x = 5 and sys.getsizeof(x) returns 28 (that is binary representation in 28 bit).

For now I am trying to flip the first bit to 1 by using x|=(1<<27)but it returns 134217733.

I was just wondering whether it needs to be some negative number? (not -5)

Is there anything wrong with what I am doing?


回答1:


You can't switch a Python int from positive to negative the way you're trying to, by just flipping a bit in its representation. You're assuming it's stored in a fixed-length two's complement representation. But integers in Python 3 are not fixed-length bit strings, and they are not stored in a two's complement representation. Instead, they are stored as variable-length strings of 30- or 15-bit "digits", with the sign stored separately (like a signed-magnitude representation). So the "lowest-level" way to negate a Python int is not with bit operations, but with the unary - operator, which will switch its sign. (See the end of this answer for details from the Python 3 source.)

(I should also mention that sys.getsizeof() does not tell you the number of bits in your int. It gives you the number of bytes of memory that the integer object is using. This is also not the number of bytes of the actual stored number; most of those bytes are for other things.)


You can still play around with two's complement representations in Python, by emulating a fixed-length bit string using a positive int. First, choose the length you want, for example 6 bits. (You could just as easily choose larger numbers like 28 or 594.) We can define some helpful constants and functions:

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

Now we can do something like you were trying to:

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

Which shows that turning on the high bit for the number 2 in a 6-bit two's complement representation turns the number into -30. This makes sense, because 26-1 = 32, so the lowest integer in this representation is -32. And -32 + 2 = -30.


If you're interested in the details of how Python 3 stores integers, you can look through 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);
}

you can see that all it does in the normal case is negate the Py_SIZE() value of the integer object. Py_SIZE() is simply a reference to the ob_size field of the integer object. When this value is 0, the integer is 0. Otherwise, its sign is the sign of the integer, and its absolute value is the number of 30- or 15-bit digits in the array that holds the integer's absolute value.




回答2:


Negative number representations in python :

Depending on how many binary digit you want, subtract from a number (2n):

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

Function to generate two's compliment(negative number):

def to_twoscomplement(bits, value):
    if value < 0:
        value = ( 1<<bits ) + value
    formatstring = '{:0%ib}' % bits
    return formatstring.format(value)

Output:

>>> to_twoscomplement(16, 3)
'0000000000000011'
>>> to_twoscomplement(16, -3)
'1111111111111101'

Refer : two's complement of numbers in python



来源:https://stackoverflow.com/questions/37135106/what-is-good-way-to-negate-an-integer-in-binary-operation-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!