Тип Python long vs C 'long long'

Я хотел бы представить значение в виде 64-битного подписанного long, так что значения больше (2 ** 63) -1 представлены как отрицательные, однако Python long имеет бесконечную точность. Есть ли "быстрый" способ достичь этого?

Ответ 1

Вы можете использовать ctypes.c_longlong:

>>> from ctypes import c_longlong as ll
>>> ll(2 ** 63 - 1)
c_longlong(9223372036854775807L)
>>> ll(2 ** 63)
c_longlong(-9223372036854775808L)
>>> ll(2 ** 63).value
-9223372036854775808L

Это действительно только вариант, если вы точно знаете, что signed long long будет иметь ширину 64 бит на целевой машине.

Изменить: идея jorendorff определения класса для 64-битных чисел является привлекательной. В идеале вы хотите свести к минимуму количество явных созданий классов.

Используя c_longlong, вы можете сделать что-то вроде этого ( note: только Python 3.x!):

from ctypes import c_longlong

class ll(int):
    def __new__(cls, n):
        return int.__new__(cls, c_longlong(n).value)

    def __add__(self, other):
        return ll(super().__add__(other))

    def __radd__(self, other):
        return ll(other.__add__(self))

    def __sub__(self, other):
        return ll(super().__sub__(other))

    def __rsub__(self, other):
        return ll(other.__sub__(self))

    ...

Таким образом, результат ll(2 ** 63) - 1 действительно будет 9223372036854775807. Эта конструкция может привести к штрафу за производительность, хотя, поэтому в зависимости от того, что вы хотите сделать точно, определение класса, такого как выше, может не стоить того. В случае сомнений используйте timeit.

Ответ 2

Можете ли вы использовать numpy? Он имеет тип int64, который делает именно то, что вы хотите.

In [1]: import numpy

In [2]: numpy.int64(2**63-1)
Out[2]: 9223372036854775807

In [3]: numpy.int64(2**63-1)+1
Out[3]: -9223372036854775808

Он прозрачен для пользователей, в отличие от примера ctypes, и он закодирован в C, поэтому он будет быстрее, чем запуск собственного класса в Python. Numpy может быть больше, чем другие решения, но если вы проводите численный анализ, вы оцените его.

Ответ 3

Самая быстрая вещь - это, вероятно, обрезать результат до 64 бит:

def to_int64(n):
    n = n & ((1 << 64) - 1)
    if n > (1 << 63) - 1:
        n -= 1 << 64
    return n

Вы можете, конечно, определить свой собственный числовой тип, который автоматически делает это каждый раз, когда вы выполняете какую-либо арифметическую операцию:

class Int64:
    def __init__(self, n):
        if isinstance(n, Int64):
            n = n.val
        self.val = to_int64(n)

    def __add__(self, other):
        return Int64(self.val + other)

    def __radd__(self, other):
        return Int64(other + self.val)

    def __sub__(self, other):
        return Int64(self.val - other)

    ...

но это не особенно "быстро" реализовать.

Ответ 4

Посмотрите на модуль ctypes, он используется для вызова иностранных библиотек DLL/библиотек из python. Существуют некоторые типы данных, которые соответствуют типам C, например

класс c_longlong