Как обрабатывать произвольно большие целые числа

Я работаю над языком программирования, и сегодня я понял, что могу скомпилировать факториальную функцию (рекурсивную), однако из-за максимального размера целого числа самое большое, что я могу получить, это факториал (12). Каковы некоторые методы обработки целых чисел произвольного максимального размера. В настоящее время язык работает путем перевода кода на C++.


person Alex Gaynor    schedule 21.11.2008    source источник
comment
То, что вы ищете, часто называют Bignum, в основном это какой-то класс, который обрабатывает произвольно большие целые числа.   -  person Kendall Helmstetter Gelner    schedule 22.11.2008
comment
Вы не можете обрабатывать целые числа произвольного максимального размера, поскольку максимальный размер ограничен доступной памятью, а ни у одного компьютера нет бесконечной памяти. Вы можете написать библиотеки для обработки такого большого числа, которое вам может понадобиться, но подумал, что стоит отметить, что у этих подходов есть технические ограничения.   -  person Dominic Rodger    schedule 04.10.2009
comment
Сказав это, вы можете хранить ужасно большое число с 4 ГБ ОЗУ (и ТБ на жестком диске, если вы действительно хотите), так что это только философское возражение.   -  person Dominic Rodger    schedule 04.10.2009


Ответы (7)


Если вам нужно больше 32 бит, вы можете рассмотреть возможность использования 64-битных целых чисел (long long) или использовать или написать математическую библиотеку произвольной точности, например. GNU MP.

person Barry Kelly    schedule 21.11.2008

Если вы хотите создать свою собственную библиотеку произвольной точности, см. Получисловые алгоритмы Кнута, том 2 его великого труда.

person John D. Cook    schedule 21.11.2008
comment
+1 для Кнута - в противном случае вы можете пропустить редкий угловой случай деления. - person Steve Gilham; 04.10.2009

Если вы встраиваете это в язык (я думаю, в целях обучения), я думаю, что, вероятно, напишу небольшую библиотеку BCD. Просто сохраните свои числа BCD внутри байтовых массивов.

Фактически, с сегодняшними гигантскими возможностями хранения вы могли бы просто использовать массив байтов, где каждый байт просто содержит цифру (0-9). Затем вы пишете свою собственную процедуру для сложения, вычитания, умножения и деления массивов байтов.

(Разделить — это сложно, но я уверен, что вы можете найти какой-нибудь код где-нибудь.)

Я могу дать вам псевдокод, похожий на Java, но на данный момент я не могу сделать C++ с нуля:

class BigAssNumber {
    private byte[] value;

    // This constructor can handle numbers where overflows have occurred.
    public BigAssNumber(byte[] value) {
        this.value=normalize(value);
    }

    // Adds two numbers and returns the sum.  Originals not changed.
    public BigAssNumber add(BigAssNumber other) {
        // This needs to be a byte by byte copy in newly allocated space, not pointer copy!
        byte[] dest = value.length > other.length ? value : other.value;         

        // Just add each pair of numbers, like in a pencil and paper addition problem.
        for(int i=0; i<min(value.length, other.value.length); i++)
            dest[i]=value[i]+other.value[i];

        // constructor will fix overflows.
        return new BigAssNumber(dest);
    }

    // Fix things that might have overflowed  0,17,22 will turn into 1,9,2        
    private byte[] normalize(byte [] value) {
        if (most significant digit of value is not zero)
            extend the byte array by a few zero bytes in the front (MSB) position.

        // Simple cheap adjust.  Could lose inner loop easily if It mattered.
        for(int i=0;i<value.length;i++)
            while(value[i] > 9) {
                value[i] -=10;
                value[i+1] +=1;
            }
        }
    }
}

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

person Bill K    schedule 21.11.2008
comment
И если вы не делаете это для школы, просто возьмите где-нибудь код BigInteger и используйте его. - person Bill K; 22.11.2008

В C++ нет простого способа сделать это. Вам придется использовать внешнюю библиотеку, такую ​​как GNU Multiprecision, или использовать другой язык, изначально поддерживающий произвольно большие целые числа. например Питон.

person Adam Rosenfield    schedule 21.11.2008
comment
Я думаю, это довольно легко. GMP поставляется вместе с красивым заголовком C++ - person sellibitze; 04.10.2009

Другие авторы дали ссылки на библиотеки, которые сделают это за вас, но похоже, что вы пытаетесь встроить это в свой язык. Моя первая мысль: вы уверены, что вам нужно это сделать? Большинство языков будут использовать дополнительную библиотеку, как предлагали другие.

Предполагая, что вы пишете компилятор и вам нужна эта функция, вы можете реализовать целочисленные арифметические функции для произвольно больших значений в ассемблере.

Например, простая (но неоптимальная) реализация будет представлять числа как двоично-десятичные числа. Арифметические функции могут использовать те же алгоритмы, что и вы, если бы занимались математикой с помощью карандаша и бумаги.

Кроме того, рассмотрите возможность использования специального типа данных для этих больших целых чисел. Таким образом, "обычные" целые числа могут использовать стандартную 32-битную арифметику.

person Clayton    schedule 21.11.2008
comment
Что люди одержимы BCD? Nodoby попросил об этом здесь. - person sellibitze; 04.10.2009

Мой предпочтительный подход состоял бы в том, чтобы использовать мой текущий тип int для 32-битных целых чисел (или, возможно, изменить его на внутренне, чтобы он был long long или что-то в этом роде, если он может продолжать использовать те же алгоритмы), а затем, когда он переполняется, изменить его на хранение в виде бигнума, будь то мое собственное создание или использование внешней библиотеки. Тем не менее, я чувствую, что мне нужно будет проверять переполнение при каждой арифметической операции, что примерно в 2 раза больше накладных расходов на арифметические операции. Как я мог это решить?

person Alex Gaynor    schedule 21.11.2008
comment
Не беспокойтесь слишком о производительности. Закодируйте его, не обращая внимания на производительность, а затем приступайте к рефакторингу, если вы не можете выполнить какой-либо контрольный показатель. - person Bill K; 22.11.2008
comment
Да, вы даже можете преобразовать пузырьковую сортировку в сортировку слиянием... И вам, конечно же, нужен хороший маркетинговый имидж по сравнению с другими, чтобы продавать свои упакованные в термоусадочную пленку коробки вашего объектно-ориентированного языка общего назначения большим мальчикам. Какой? это не универсал? - person artificialidiot; 22.11.2008
comment
Проблемы, которые вы описываете, - это то, почему я предложил создать новый тип данных. C++, Java и т. д. не будут автоматически преобразовывать 16-битное целое число в 32-битное, если умножение переполняется, так зачем вам это делать. С другой стороны, если это задокументированное требование, вам просто придется принять удар по производительности. - person Clayton; 22.11.2008

Если бы я реализовал свой собственный язык и хотел бы поддерживать числа произвольной длины, я бы использовал целевой язык с концепцией переноса/заимствования. Но так как нет HLL, который реализует это без серьезных последствий для производительности (например, исключений), я обязательно реализую это на ассемблере. Вероятно, потребуется одна инструкция (как в JC в x86), чтобы проверить переполнение и обработать его (как в ADC в x86), что является приемлемым компромиссом для языка, реализующего произвольную точность. Затем я буду использовать несколько функций, написанных на ассемблере, вместо обычных операторов, если вы можете использовать перегрузку для более элегантного вывода, даже лучше. Но я не ожидаю, что сгенерированный C++ будет поддерживаться (или предназначен для обслуживания) в качестве целевого языка.

Или просто используйте библиотеку, в которой больше наворотов, чем вам нужно, и используйте ее для всех своих номеров.

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

person artificialidiot    schedule 21.11.2008