Python: Есть ли способ предотвратить автоматическое преобразование int в long int? - PullRequest
13 голосов
/ 06 декабря 2010

Python более строго типизирован, чем другие языки сценариев. Например, в Perl:

perl -E '$c=5; $d="6"; say $c+$d'   #prints 11

Но в Python:

>>> c="6"
>>> d=5
>>> print c+d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot concatenate 'str' and 'int' objects

Perl проверит строку и преобразует ее в число, а операторы + - / * ** работают с числом, как вы ожидаете. PHP похож.

Python использует + для объединения строк, поэтому попытка операции c+d завершается неудачно, поскольку c является строкой, d int. Python имеет более сильный смысл числовых типов , чем Perl. ОК, я могу с этим справиться.

Но подумайте:

>>> from sys import maxint
>>> type(maxint)
<type 'int'>
>>> print maxint
9223372036854775807
>>> type(maxint+2)
<type 'long'>
>>> print maxint+2
9223372036854775809
>>> type((maxint+2)+maxint)
<type 'long'>
>>> print ((maxint+2)+maxint)
18446744073709551616

Теперь Python будет autopromote из целого числа, которое в данном случае имеет длину 64 бита (OS X, python 2.6.1), в целое число Python с произвольной точностью. Несмотря на то, что типы не совпадают, они похожи, и Python позволяет использовать обычные числовые операторы. Обычно это полезно. Это полезно, например, для сглаживания различий между 32 и 64 битами.

Преобразование из int в long является одним из способов:

>>> type((maxint+2)-2)
<type 'long'>

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

>>> print maxint**maxint        # execution so long it is essentially a crash

Итак, мой вопрос: есть ли способ победить или запретить автоматическое продвижение Python int на Python long?

Редактировать, продолжение:

Я получил несколько комментариев в форме «с какой стати вы хотели бы иметь поведение переполнения в стиле C?» Проблема заключалась в том, что этот конкретный фрагмент кода работал нормально на 32 битах в C и Perl (с use int) с поведением переполнения Си. Произошла неудачная попытка перенести этот код на Python. Различное поведение переполнения в Python оказывается (частью) проблемы. В коде смешаны многие из этих различных идиом (C, Perl, немного Python) (и эти комментарии смешаны), так что это было сложно.

По сути, выполняемый анализ изображения представляет собой высокочастотный фильтр на основе диска для выполнения аналогичного сравнения изображений. Часть фильтра верхних частот имеет целочисленное умножение двух больших полиномов. Переполнение было, по сути, логикой «все равно, это большое ...», поэтому результат был таким, как предполагалось при переполнении на основе Си. Таким образом, использование правила Хорнера со временем O (n 2 ) было пустой тратой, так как большие полиномы были бы просто "большими" - грубая форма арифметики насыщенности вершины каротины.

Изменение полиномиального умножения на основе цикла на форму БПФ, вероятно, значительно быстрее. БПФ работает близко к линейному времени против O (n 2 ) для умножения полинома по правилу Хорнера. Переход с диска в память также ускорит это. Изображения не очень большие, но оригинальный код был написан в то время, когда они считались "огромными !!!" Владелец кода не совсем готов отказаться от своего любимого кода, так что посмотрим. Вероятно, «правильный ответ» для него - просто оставить Perl или C, если он хочет этот код.

Спасибо за ответы. Я не знал о десятичном модуле Python, и это, казалось, было ближе всего к тому, о чем я спрашивал - хотя в этом случае нужно решить и другие проблемы!

Ответы [ 6 ]

5 голосов
/ 06 декабря 2010

Если вы хотите, чтобы арифметические переполнения переполнялись, например, в пределах. 32 бита, вы можете использовать, например, numpy.uint32.

Это выдает предупреждение при переполнении.

>>> import numpy
>>> numpy.uint32(2**32-3) + numpy.uint32(5)
Warning: overflow encountered in ulong_scalars
2

Я проверил его скорость:

>\python26\python.exe -m timeit "2**16 + 2**2"
1000000 loops, best of 3: 0.118 usec per loop

>\python26\python.exe -m timeit "2**67 + 2**65"
1000000 loops, best of 3: 0.234 usec per loop

>\python26\python.exe -m timeit -s "import numpy; numpy.seterr('ignore')" "numpy.uint32(2)**numpy.uint32(67) + numpy.uint32(2)**numpy.uint32(65)"
10000 loops, best of 3: 34.7 usec per loop

Это не очень хорошо для скорости.

4 голосов
/ 07 декабря 2010

Итак, вы хотите выбросить Единый Истинный Путь и уйти в прошлое при переполнении.Ты глупый.

Нет ничего хорошего в переполнении в стиле C / C ++ / C # / Java. Надежно не возникает условие ошибки .Для C и C99 это «неопределенное поведение» в ANSI и POSIX (C ++ требует по модулю возврата), и это известная угроза безопасности.Почему вы хотите этого?

Лучше всего использовать метод Python плавного перетекания в long.Я полагаю, что это то же самое поведение, адаптируемое в Perl 6.

Вы можете использовать десятичный модуль 1012 *, чтобы получить более конечные переполнения:

>>> from decimal import *
>>> from sys import maxint
>>> getcontext()
Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999, capitals=1,
flags=[], traps=[DivisionByZero, Overflow, InvalidOperation])

>>> d=Decimal(maxint)
>>> d
Decimal('9223372036854775807')
>>> e=Decimal(maxint)
>>> f=d**e
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/decimal.py", line 2225, in __pow__
    ans = ans._fix(context)
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/decimal.py", line 1589, in _fix
    return context._raise_error(Overflow, 'above Emax', self._sign)
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/decimal.py", line 3680, in _raise_error
    raise error(explanation)
decimal.Overflow: above Emax

Вы можете установитьТочность и граничные условия с десятичными классами и переполнение почти мгновенно.Вы можете установить то, что вы ловите за.Вы можете установить свои максимальные и минимальные.Действительно - Как это становится лучше, чем это?(Честно говоря, я не знаю об относительной скорости, но я подозреваю, что она быстрее чем тупая, но медленнее, чем родные целые числа ...)

Для вашей конкретной проблемы обработки изображений это звучит как естественнаяприложение, чтобы рассмотреть некоторую форму арифметики насыщения .Вы также можете подумать, если у вас есть переполнения по 32 арифметике, проверьте операнды по пути в очевидных случаях: pow, **, *.Вы можете рассмотреть перегруженные операторы и проверить условия, которые вам не нужны.

Если не работают десятичные, насыщенные или перегруженные операторы - вы можете написать расширение.Небеса помогут вам, если вы хотите выбросить путь переполнения Python, чтобы перейти в ретро ...

2 голосов
/ 06 декабря 2010

Вы можете принудительно вернуть значения к нормальным значениям int s, если время от времени включаете num = int(num) в свой алгоритм. Если значение длинное, но соответствует внутреннему типу int, оно будет понижено до int. Если значение не помещается в нативный int, оно останется длинным.

2 голосов
/ 06 декабря 2010

Int против long - историческое наследие - в python 3 каждое int - это «long».Если скорость вашего скрипта ограничена вычислениями int, скорее всего, вы делаете это неправильно.

Чтобы дать вам правильный ответ, нам нужна дополнительная информация о том, что вы пытаетесь сделать.

1 голос
/ 06 декабря 2010

Ну, если вас не волнует точность, вы можете использовать все свои математические операции по модулю maxint.

1 голос
/ 06 декабря 2010

Я не знаю, будет ли это быстрее, но не обязательно, но вы можете использовать массивы из одного элемента вместо целых.

Если конкретный расчет, который вас интересует, это целочисленное возведение в степень,некоторые выводы, которые мы можем нарисовать:

def smart_pow(mantissa, exponent, limit=int(math.ceil(math.log(sys.maxint)/math.log(2)))):
    if mantissa in (0, 1):
        return mantissa
    if exponent > limit:
        if mantissa == -1: 
            return -1 if exponent&1 else 1
        if mantissa > 1:
            return sys.maxint
        else: 
            return (-1-sys.maxint) if exponent&1 else sys.maxint
    else: # this *might* overflow, but at least it won't take long
        return mantissa ** exponent
...