2015-07-21 2 views
3

У меня очень большое число в базе n (n указан пользователем), хранящийся в виде массива с каждым элементом, представляющим цифру. u[0] - самая высокая цифра, u[1] - вторая по величине, u[-1] - самая низкая цифра и так далее. Ведущие нули понимаются как бессмысленные: например, если n составляет 8, [0, 0, 0, 4, 7, 3] эквивалентен [4, 7, 3] и оба равны (473) в основании 8 или 315 в основании 10 или 13B в шестнадцатеричном виде или [1, 59] в качестве массива байтов.Преобразование очень большого базового числа n в байты

Я хочу преобразовать это в массив байтов, соответствующих представлению базового 256 того же числа с минимальными начальными нулями. У меня есть следующий код для этого:

def base_n_to_byte_array(digits, from_base): 
    """ Converts a base n number to a byte array. 

    :param digits: Digits of the number, starting from highest. 
    :param from_base: Base in which the number is given. 
    """ 

    x = 0 
    n = len(digits) 
    for i in range(0, len(digits)): 
     x += digits[i] * int(math.pow(from_base, n - i - 1)) 

    min_length = max(math.ceil(math.log(x, 256)), 1) 
    byte_array = x.to_bytes(min_length, byteorder='big') 
    return byte_array 

Это работает для меньших чисел (несколько сотен цифр). Однако выясняется, что math.pow довольно ограничен, например, если мы используем базу 8, math.pow(8, 341) - это самая высокая мощность, которую я могу получить, и math.pow(8,342) с ошибкой OverflowError: math range error.

Я знаю, что общий способ работы с большими числами - представлять их как плавающие точки, но в этом случае я использую этот код для кодирования/декодирования двоичных файлов в альтернативные представления (например, trytes). Поэтому, если из-за потери точности изменятся менее значимые байты, многие данные будут повреждены, поэтому я не могу использовать приблизительный расчет мощности - мне нужно, чтобы результат был точным.

Как я могу решить эту проблему? Есть ли версия math.pow, которая не переполняется? Есть ли более эффективный алгоритм преобразования баз данных, который я пропускаю?

+0

Что вам нужно [длинная арифметика] (https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic). – WhatsUp

+0

@WhatsUp Python делает это автоматически, если вы не плаваете. – Teepeemm

+0

@WhatsUp Я знаю, что это один из моих вариантов, я спрашиваю, как это сделать в Python3. – Superbest

ответ

4

Есть ли версия math.pow, которая не переполнена?

Попробуйте использовать встроенный оператор возведения в степень, **. AFAIK у него нет тех же ограничений, что и math.pow.

>>> math.pow(8,342) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
OverflowError: math range error 

>>> 8**342 
719077253944926363091722076315609893447190791576922629093720324630930703222003852530833909289630144084480455519485573430635159075257666489971389722557896497511071573699461941105208878404984376477812331808340023075352602729369851525895652442163308948653402042738345192959788983753918865219341425318496896548864L 
+1

В документации для ['math.pow'] (https://docs.python.org/3/library/math.html#math.pow) указано, что' pow' (aka '**') не имеют те же ограничения. – Teepeemm

+1

Отлично! В этом случае я отменяю свой «AFAIK» и сердечно одобряю как «pow», так и «**». – Kevin