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

Я ищу очень простой класс C ++, реализующий целое число без знака с произвольной точностью и только оператор пост-инкремента.

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

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

#include <vector>
#include <string>
#include <algorithm>

class UNat
{
public:
static const char base = 10;
UNat( const char* n )
{
std::string s(n);
std::reverse(s.begin(),s.end());
for ( size_t i = 0; i < s.length(); i++ ) {
n_.push_back(s[i]-'0');
}
}

UNat& operator++(int)
{
bool carry = false;
bool finished = false;

for ( size_t i = 0; i < n_.size() && !finished; i++ ) {
n_[i] = add(n_[i],1,carry);
if ( carry ) {
finished = false;
} else {
finished = true;
}
}
if ( carry ) {
n_.push_back(1);
}
return *this;
}

std::string to_string() const
{
std::string r(n_.begin(), n_.end());
std::reverse(r.begin(),r.end());
std::for_each(r.begin(), r.end(), [](char& d) { d+='0';});
return r;
}

private:
char add( const char& a, const char& b, bool& carry )
{
char cc = a + b;
if ( cc >= base ) {
carry = true;
cc -= base;
} else {
carry = false;
}
return cc;
}
std::vector< char > n_;
};

std::ostream& operator<<(std::ostream& oss, const UNat& n)
{
oss << n.to_string();
return oss;
}

#include <iostream>

int main()
{
UNat n("0");
std::cout << n++ << "\n";
std::cout << UNat("9")++ << "\n";
std::cout << UNat("99")++ << "\n";
std::cout << UNat("19")++ << "\n";
std::cout << UNat("29")++ << "\n";
std::cout << UNat("39")++ << "\n";
return 0;
}

0

Решение

Чтобы избежать возврата измененного значения, сохраните локальную копию и верните ее:

UNat operator++(int)
{
UNat copy = *this;
// ....
return copy;
} // don't return by reference

Сравнительно, оператор префикса делает вернуться по ссылке.

UNat& operator++ ()
{
// ....
return *this;
}

Некоторые советы и рекомендации от Произвольно-точная арифметика Объяснение:

1 / При добавлении или умножении чисел предварительно выделяйте максимальное пространство
необходимо потом уменьшить, если вы обнаружите, что это слишком много. Например,
добавление двух 100-значных цифр (где цифра — целое число) никогда не даст
Вам больше 101 цифры. Умножьте 12-значное число на 3-значное
число никогда не будет генерировать более 15 цифр (добавить количество цифр).

Альтернативная реализация для вашей функции сложения может выглядеть так:

c->lastdigit = std::max(a->lastdigit, b->lastdigit)+1;
carry = 0;

for (i=0; i<=(c->lastdigit); i++) {
c->digits[i] = (char) (carry+a->digits[i]+b->digits[i]) % 10;
carry = (carry + a->digits[i] + b->digits[i]) / 10;
}
1

Другие решения

Код реализует то, что обычно известно как двоичное кодированное десятичное число (BCD). Очень просто, и, как вы говорите, реализация может быть намного проще, если вы хотите только увеличивать и не нуждаться в общем добавлении. Чтобы упростить еще больше, используйте внутренние символьные представления для цифр '0' через '9' вместо значений 0 через 9, И для еще одного упрощения, храните символы в std::string:

for (int index = 0; index < digits.size(); ++index) {
if (digits[index] != '9') {
++digits[index];
break;
}
digits[index] = '0';
}
if (index == digits.size())
digits.push_back('1');

Это делает потоковую вставку почти тривиальной.

1