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

0

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

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

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

#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;
}
  • 4
    Предполагается, что постинкремент увеличивает значение и возвращает исходное значение в качестве результата выражения. Кажется, что ваш постинкрементный оператор возвращает измененное значение. Это проблема, с которой вы хотите помочь?
  • 0
    Я предполагаю, что если все, что вы делаете, это инкремент, перенос по ряду не так уж и плох ... Возможно, было бы хорошо делать вещи на 32-битных или 64-битных примитивах вместо 8-битных, но 8-битных делает это простым ,
Показать ещё 4 комментария
Теги:
arbitrary-precision

2 ответа

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');

Это делает процесс ввода потока почти тривиальным.

  • 0
    Если вы хотите использовать разные радиусы, вы можете сделать это, используя двоичные и просто и if-then-else для current-bit-value. Но все это делает медленную версию бесконечной точности.
  • 0
    @IraBaxter - есть много наворотов, которые можно добавить. Вопрос был в том, как сделать это простым, и это самое простое, что я вижу. В частности, поддержка оснований больше 10 означает написание гораздо более сложного кода для приращения отдельных элементов.
Показать ещё 5 комментариев
1

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

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

Сравнительно оператор префикса возвращается по ссылке.

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

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

1/При добавлении или умножении чисел предварительно назначьте максимальное пространство, а затем уменьшите его, если вы найдете слишком много. Например, добавление двух цифр 100- "digit" (где цифра - int) никогда не даст вам более 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;
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню