Сейчас я работаю над концепцией и пишу псевдокод, когда задаю этот вопрос. Я думаю о создании довольно простого и простого в использовании интерфейса класса для представления BigInts. Я думаю создать пару простых структур с базовыми свойствами и членами, которые будет использовать класс BigInt. Например, вместо того, чтобы класс BigInt обрабатывал отрицательные значения напрямую, он содержал бы структуру Sign, и эта структура в основном содержала бы либо значение 0 или 1, либо в основном логический тип для указания, является ли этот BigInt положительным или отрицательным. После создания я намерен заставить класс генерировать позитив по умолчанию. Я также хотел бы иметь структуру, которая представляет цифры, где есть два варианта. Первый вариант имеет цифры 0-9, а второй, который наследует оригинал, но также включает в себя A-F. Таким образом, класс, который будет классом-шаблоном, но только когда-либо имеет два допустимых типа, будет поддерживать использование десятичного и шестнадцатеричного типов. Все математические операторы будут определены вне класса, и в зависимости от его предполагаемого типа он будет вызывать и выполнять соответствующую версию функции. Однако шестнадцатеричная часть все еще является концептуальной, поскольку я хотел бы сначала запустить и запустить десятичную версию. Эти вспомогательные классы могут выглядеть примерно так:
class Sign {
private:
bool isNegative_ { false };
char sign_;
public:
Sign() : isNegative_( false ) {
sign_ = '+';
}
Sign( const bool isNegative ) : isNegative_( isNegative ) {
sign_ = isNegative_ ? '-' : '+';
};
Sign( const char sign ) {
if ( sign == '+' || sign == '\0' || sign == ' ' ) {
isNegative_ = false;
} else if ( sign == '-' ) {
isNegative_ = true;
} else {
// throw error improper character.
}
}
bool isPostive() const { return !isNegative_; }
bool isNegative() const { return !isNegative; }
char sign() const { return sign_; }
char negate() {
isNegative_ = !isNegative_;
sign_ = isNegative_ ? '+' : '-';
return sign_;
}
};
// NST = NumberSystemType
class enum NST { Decimal = 10, Hexadecimal = 16 };
template<class NT> // NT = NumberType
class Digit {
private:
NST nst_; // determines if Decimal or Hexadecimal
};
// Specialization for Decimal
template<NST::Decimal> // Should be same as template<10>
class Digit {
// would like some way to define 4 bits to represent 0-9; prefer not to
// use bitfields, but I think a char or unsigned char would be wasting
// memory using twice as much for what is needed. Not sure what to do here...
// maybe std::bitset<>...
};
template<NST::Hexadecimal> // Should be same as template<16>
class Digit : public Digit<Decimal> { // Also inherits all of The first specialization.
// same as above only needs 4 bits to represent values form 0-F
// A half char would be excellent but does not exist in c++... and most
// programming language's types.
// still thinking of maybe std::bitset<>...
};
Основное различие между ними состоит в том, что первая специализация допускает только значения цифр от 0-9 и сами цифры 0-9, где вторая не имеет такого ограничения, но также допускает использование af и / AF в любом случае. является действительным. Я также могу включить const char * для обозначения шестнадцатеричного префикса 0x
, который будет добавлен к любому содержанию для отображения.
Мне нравится этот подход к проектированию, поскольку я хотел бы сохранить фактические арифметические функции и операторы класса BigInt в качестве отдельных шаблонов функций, поскольку класс BigInt может поддерживать как специализированные типы шаблонов Decimal, так и Hexadecimal. Также в будущем, если все пойдет правильно, я бы также хотел добавить поддержку для работы с комплексными числами.
Класс BigInt будет выглядеть так:
template<class NT>
BigInt {
private:
Sign sign_;
Digit<NT> carryDigit_;
std::vector<Digit<NT>> value_;
// May contain some functions such as setters and getters only
// Do not want the class to be modifying itself except for assignment only.
};
И, как указано выше, это также будет специализировано для десятичного и шестнадцатеричного типов, однако, если кто-то создает экземпляр BigInt <> myBigInt, по умолчанию он должен быть десятичным!
Для данных, содержащихся в векторе. Я хотел бы хранить цифры в обратном порядке того, что читает. Таким образом, если их число 345698
во внутреннем векторе BigInt, оно будет сохранено как 896543
. Причина этого заключается в том, что когда мы выполняем арифметические операции в математике, мы работаем от наименее значимого до самого значимого, начиная с правой части слева от десятичной точки, что не имеет значения, поскольку это класс только для BigInt, и мы работаем в направлении левый. Однако, если мы сохраним каждую цифру, которая может быть только 0-9 в каждом элементе вектора вышеупомянутого класса в правильном порядке, и мы используем внешнюю функцию operator + (), это будет сложно сделать для одного BigInt для другого ... пример:
Basic Arithmetic R - L | Vector storing standard
12345 <1,2,3,4,5>
+ 678 <6,7,8>
------
13023
Здесь индексы <5> & <8> не совпадают, поэтому сложно понять, как добавить значение с несколькими цифрами к одному со многими. Мой подход заключается в том, что если мы будем хранить число в обратном порядке:
| Vector stored in reverse
<5,4,3,2,1>
<6,7,8>
Тогда сложение становится простым! Все, что нам нужно сделать, это добавить каждую цифру от обоих векторов BigInt к тому же значению индекса. И мы используем цифру переноса для перехода к следующему полю. Результирующий BigInt вернется с размером, который по крайней мере равен или больше, чем самый большой из двух BigInts. Если значение carryDigit имеет значение, то операция сложения на следующей итерации будет включать 3 сложения вместо двух. Теперь при получении BigInt для отображения мы можем вернуть либо вектор>, за исключением того, что когда пользователь получает его, это не «BigInt», это вектор цифр, и он также находится в правильном порядке. Это может даже быть возвращено строковым представлением. Этот класс может быть создан с помощью вектора>, и он изменит порядок, когда он сохранит его внутри.
Это общая концепция моего класса BigInt, мне просто интересно, если это хороший план дизайна, если он будет считаться эффективным, и я думаю, главный вопрос, который у меня есть, о том, что использовать для хранения фактического цифры в классе Digit ... Было бы std::bitset<>
целесообразно сохранить отпечаток памяти или просто лучше использовать char
и не беспокоиться о дополнительном пространстве с преимуществом простоты реализации?