2008-11-06 8 views
69

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

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

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

+10

Если это упражнение по программированию, начните кодирование. Вы не можете выполнить какое-либо упражнение иначе! – 2008-11-06 16:19:37

+3

Первое, что есть, цифры в цифрах, но думаю в терминах базы 2^32 (4 миллиарда нечетных отдельных цифр). Возможно, в наши дни даже база 2^64. Во-вторых, всегда работайте с целыми числами без знака. Вы можете сделать двойной набор для подписанных больших целых чисел самостоятельно, но если вы попытаетесь выполнить обработку переполнения и т. Д. Со значащими целыми числами, вы столкнетесь с неопределенными стандартами проблем bahaviour. – Steve314 2011-02-14 21:42:12

+3

Что касается алгоритмов - для базовой библиотеки те, которые вы узнали в школе, примерно совпадают. – Steve314 2011-02-14 21:42:58

ответ

35

Вещи рассмотреть для большого класса Int:

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

  2. I/O операторы: >>, < < Это , где вы выяснить, как правильно Создайте свой класс с пользовательского ввода и как его форматировать для вывода.

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

5

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

0

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

4

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

+0

Если вы хотите представить числа в десятичной форме (т. Е. Для простых смертных), то алгоритм 0-9 на полубайт проще. Просто бросьте хранилище. – dmckee 2008-11-06 16:26:56

+0

Вы считаете, что проще выполнять алгоритмы BCD, чем их обычные двоичные копии? – Eclipse 2008-11-06 19:07:20

+2

База AFAIK 10 часто используется, потому что преобразование больших чисел в базу 255 (или что-то не в 10) от 10 до 10 является дорогостоящим, а вход и выход ваших программ обычно будут в базе 10. – Tobi 2008-11-10 16:21:22

5

Интересно, что я только что просмотрел видео об этом, прежде чем я увидел ваш вопрос. Here is the link. Предоставлено Конгрессом по связям с Хаосом.

2

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

1

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

У чипов серии Motorola 68K это было. Не то, чтобы я горький или что-то еще.

3

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

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

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; 
} 

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

0

Мое начало было бы иметь массив целых чисел произвольного размера, используя 31 бит и 32n'd как переполнение.

Стартер op будет ДОБАВИТЬ, а затем, НЕПРЕРЫВНО, используя дополнение 2. После этого вычитание протекает тривиально, и как только у вас есть add/sub, все остальное выполнимо.

Возможно, существуют более сложные подходы. Но это был бы наивный подход от цифровой логики.

2

Посмотрите, как GMP реализует свои алгоритмы here.

25

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

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

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

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

Удачи!

40

Веселый вызов.:)

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

Рассмотрим двоичный характер типа «int». Подумайте о том, как использовать простые двоичные операции для эмуляции того, что делают схемы в вашем CPU, когда они добавляют вещи. В случае, если вам интереснее более подробно, подумайте о том, чтобы прочитать this wikipedia article on half-adders and full-adders. Вы будете делать что-то похожее на это, но вы можете пойти на такой низкий уровень, но, будучи ленивым, я подумал, что я просто откажусь и найду еще более простое решение.

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

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

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

Но теперь некоторые алгоритмы работы с числами. Вы можете сделать это на логическом уровне, но мы будем использовать эту магическую мощность процессора для вычисления результатов. Но то, что мы возьмем на себя из логической иллюстрации Half- и FullAdders, это то, как она относится к переносам. В качестве примера рассмотрим, как вы бы применили оператор + =. Для каждого номера в BigInt <> :: value_ вы должны добавить их и посмотреть, приведет ли результат к какой-либо форме переноса. Мы не будем делать это по-разному, но полагаемся на природу нашего BaseType (будь то длинный или 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. ;-) 

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

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

Надеюсь, что этот подход поможет!

-1

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

0

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

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

Вы должны были бы только 4 бита для одной цифры 0 - 9

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

Также при хранении всех цифр в одном 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 

 Смежные вопросы

  • Нет связанных вопросов^_^