Как реализовать big int в C++



Я хотел бы реализовать большой класс int в C++ в качестве упражнения по программированию-класс, который может обрабатывать числа больше, чем длинный int. Я знаю, что уже есть несколько реализаций с открытым исходным кодом, но я хотел бы написать свою собственную. Я пытаюсь понять, что такое правильный подход.



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



Я ищу общий подход и советы, а не фактический рабочий код.

691   15  

15 ответов:

Что нужно учитывать для большого класса int:

  1. математические операторы: +, -, /, * , % Не забывайте, что ваш класс может быть по обе стороны оператор, что операторы могут быть прикован, что один из операндов может быть int, float, double и т. д.

  2. операторы ввода/вывода:>>,

  3. преобразования / приведения: выяснить какие типы / классы ваш большой int класс должен быть конвертируемым, и как правильно обращаться с преобразование. Быстрый список будет включите двойник и поплавок, и май включить int (с правильными границами проверка) и сложный (предполагая это смогите отрегулировать ряд).

забавная задача. :)

Я предполагаю, что вы хотите целые числа произвольной длины. Я предлагаю следующий подход:

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

но прежде чем вдаваться в какие-либо алгоритмические детали о сложении, вычитании, умножении, давайте найдем некоторую структуру данных. Простой способ, конечно, хранить вещи в std:: vector.

template< class BaseType >
class BigInt
{
typedef typename BaseType BT;
protected: std::vector< BaseType > value_;
};

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

но теперь к некоторым алгоритмам по работе с числами. Вы можете сделать это на логическом уровне, но мы будем использовать эту волшебную мощность процессора для вычисления результатов. Но то, что мы возьмем на себя из логики-иллюстрации полу - и Фуллэддеров, - это то, как он имеет дело с переносами. В качестве примера рассмотрим, как бы вы реализовали оператор+=. Для каждого числа в BigInt:: value_, вы бы добавить их и посмотреть, если результат производит некоторую форму переноса. Мы не будем делать это немного мудро, но полагаемся на природу нашего базового типа (будь то длинный или int или короткий или что-то еще): он переполняется.

конечно, если сложить два числа, результат должен быть больше большего из этих чисел, верно? Если это не так, то результат захлестывают.

template< class BaseType >
BigInt< BaseType >& BigInt< BaseType >::operator += (BigInt< BaseType > const& operand)
{
  BT count, carry = 0;
  for (count = 0; count < std::max(value_.size(), operand.value_.size(); count++)
  {
    BT op0 = count < value_.size() ? value_.at(count) : 0, 
       op1 = count < operand.value_.size() ? operand.value_.at(count) : 0;
    BT digits_result = op0 + op1 + carry;
    if (digits_result-carry < std::max(op0, op1)
    {
      BT carry_old = carry;
      carry = digits_result;
      digits_result = (op0 + op1 + carry) >> sizeof(BT)*8; // NOTE [1]
    }
    else carry = 0;
  }

  return *this;
}
// NOTE 1: I did not test this code. And I am not sure if this will work; if it does
//         not, then you must restrict BaseType to be the second biggest type 
//         available, i.e. a 32-bit int when you have a 64-bit long. Then use
//         a temporary or a cast to the mightier type and retrieve the upper bits. 
//         Or you do it bitwise. ;-)

другая арифметическая операция аналогична. Черт, вы даже можете использовать стл-функторы функции std::плюс и std::минус, СТД::время и std::делит, ... но не забывай о переноске. :) Вы также можете реализовать умножение и деление с помощью ваших операторов плюс и минус, но это очень медленно, потому что это будет пересчитывать результаты, которые вы уже рассчитали в предыдущих вызовах плюс и минус в каждой итерации. Есть много хороших алгоритмов для этой простой задачи использоватьВикипедия или в интернете.

и конечно, вы должны применять стандартные операторы, такие как operator<< (просто сдвиньте каждое значение в value_ влево для n бит, начиная с value_.size()-1... О, И помните о переноске:),operator< - вы даже можете немного оптимизировать здесь, проверяя приблизительное количество цифр с size() первый. И так далее. Затем сделайте свой класс полезным, befriendig std:: ostream operator<<.

надеюсь, что этот подход полезен!

есть полный раздел об этом: [искусство компьютерного программирования, vol.2: получисловые алгоритмы, раздел 4.3 арифметика многократной точности, С. 265-318 (изд.3)]. Вы можете найти другой интересный материал в главе 4, арифметика.

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

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

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

удачи!

добавление, вероятно, должно быть сделано в стандартном линейном алгоритме времени
но для умножения вы могли бы попробовать http://en.wikipedia.org/wiki/Karatsuba_algorithm

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

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

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

например, если у вас есть структура

typedef struct {
    int high, low;
} BiggerInt;

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

BiggerInt add( const BiggerInt *lhs, const BiggerInt *rhs ) {
    BiggerInt ret;

    /* Ideally, you'd want a better way to check for overflow conditions */
    if ( rhs->high < INT_MAX - lhs->high ) {
        /* With a variable-length (a real) BigInt, you'd allocate some more room here */
    }

    ret.high = lhs->high + rhs->high;

    if ( rhs->low < INT_MAX - lhs->low ) {
        /* No overflow */
        ret.low = lhs->low + rhs->low;
    }
    else {
        /* Overflow */
        ret.high += 1;
        ret.low = lhs->low - ( INT_MAX - rhs->low ); /* Right? */
    }

    return ret;
}

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

Как говорили другие, делайте это по старинке, но не делайте этого все в базе 10. Я бы предложил сделать все это в базе 65536 и хранить вещи в массиве лонгов.

посмотреть здесь чтобы увидеть, как GMP реализует свои алгоритмы.

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

чипы серии Motorola 68K имели это. Не то чтобы мне было горько или что-то в этом роде.

использовать алгоритмы, которые вы узнали в 1-го по 4-й класс.
Начните со столбца единицы, затем десятки и так далее.

мой старт будет иметь произвольный размер массива целых чисел, используя 31 бит и 32н бы как переполнение.

стартер op будет ADD,а затем, MAKE-NEGATIVE, используя дополнение 2. После этого вычитание течет тривиально, и как только у вас есть add/sub, все остальное выполнимо.

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

можно попробовать реализовать что-то вроде этого:

http://www.docjar.org/html/api/java/math/BigInteger.java.html

вам понадобится только 4 бита на цифру 0 - 9

таким образом, значение Int позволит до 8 цифр каждый. Я решил, что буду придерживаться массива символов, поэтому я использую двойную память, но для меня она используется только 1 раз.

также при хранении всех цифр в одном int он чрезмерно усложняет его, и если что-то это может даже замедлить его.

У меня нет тестов скорости, но, глядя на java-версию BigInteger, кажется, что она делает очень много работы.

для меня, я ниже

//Number = 100,000.00, Number Digits = 32, Decimal Digits = 2.
BigDecimal *decimal = new BigDecimal("100000.00", 32, 2);
decimal += "1000.99";
cout << decimal->GetValue(0x1 | 0x2) << endl; //Format and show decimals.
//Prints: 101,000.99

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

класс C++ BigInt, который позволяет пользователю работать с целыми числами произвольной точности. http://sourceforge.net/projects/cpp-bigint/

Comments

    Ничего не найдено.