Alessandro Jacopson
Alessandro Jacopson

Reputation: 18603

Arbitrary precision unsigned integer supporting just post increment operator

I am looking for a very simple C++ class implementing an unsigned integer with arbitrary precision and just the post increment operator.

I know there are library for arbitrary precision integer arithmetic but my needs are quite simple and I prefer to avoid the weight of a full library.

It seems to me that my current implementation is still not simple enough and not elegant enough. What do you suggest?

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

Upvotes: 0

Views: 612

Answers (2)

Pete Becker
Pete Becker

Reputation: 76245

The code implements what's generally known as Binary Coded Decimal (BCD). Very simple, and, as you say, the implementation can be much simpler if you only want to increment and don't need general addition. To simplify even further, use the internal character representations for the digits '0' through '9' instead of the values 0 through 9. And for another simplification, store the characters in a 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');

This makes the stream inserter nearly trivial.

Upvotes: 1

user1508519
user1508519

Reputation:

In order to avoid returning the mutated value, save a local copy and return it:

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

Comparatively, the prefix operator does return by reference.

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

Some tips and tricks from Arbitrary-precision arithmetic Explanation:

1/ When adding or multiplying numbers, pre-allocate the maximum space needed then reduce later if you find it's too much. For example, adding two 100-"digit" (where digit is an int) numbers will never give you more than 101 digits. Multiply a 12-digit number by a 3 digit number will never generate more than 15 digits (add the digit counts).

An alternative implementation for your addition function can look like this:

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

Upvotes: 1

Related Questions