Big Integer addition, I know the theory… still rusty in practice

烂漫一生 提交于 2019-12-22 10:45:37

问题


so, I'm trying to build a simple big integer class, I've read some pages on the internet and all that, but I'm stuck. I know the theory and I know that I need a carry but all examples I've seen, they focuded more in chars and in base 10 and well, I'm using a different approach to make it a bit more faster. I would appreciate some help with the plus assignment operator, the rest of it I'll try to figure it out by myself.

#include <iostream>
#include <string>
#include <vector>

using std::cout;
using std::endl;

class big_integer {
    using box = std::vector<int unsigned>;
    box data {0};

    box split(std::string const & p_input) const {
        box output;
        for (size_t i {}; i < p_input.size(); i += 8) {
            output.push_back(stoi(p_input.substr(i, 8)));
        }
        return output;
    }

public:
    big_integer(std::string const & p_data)
        : data {split(p_data)}
    {}

    big_integer(int unsigned const p_data)
        : data {p_data}
    {}

    big_integer & operator +=(big_integer const & p_input) {
        int carry {};

        for (size_t i {}; i < data.size(); ++i) {

            //Need help here!
            //All examples I see either use primitive arrays
            //or are too esoteric for me to understand.             
            //data[i] += p_input.data[i] + carry;

        }

        return *this;
    }

    std::string to_string() const {
        std::string output;
        output.reserve(data.size() * 8);
        for (auto const i : data) {
            output.append(std::to_string(i));
        }
        return output;
    }
};

std::ostream & operator <<(std::ostream & p_output, big_integer const & p_input) {
    return p_output << p_input.to_string();
}

int main() {
    big_integer test1 {"126355316523"};
    big_integer test2 {255};

    test1 += test1;

    cout << test1 << endl;
    cout << test2 << endl;
    return 0;
}

回答1:


Right. So the basic problem is how to do unsigned + unsigned + carry to give unsigned and carry. If we consider 16-bit integers (it works the same in 32-bits, but is more typing), on the first digit, 0xFFFF + 0xFFFF == 0xFFFE + a carry of 1. On subsequent digits 0xFFFF + 0xFFFF + carry == 0xFFFF + carry. Hence carry can only be one. The algorithm is:

    unsigned lhs, rhs, carry_in;  // Input
    unsigned sum, carry_out;      // Output

    sum = lhs + rhs;
    carry_out = sum < lhs ;
    sum += carry_in;
    carry_out = carry_out || sum < lhs;

Basically, the idea is to do the addition in unsigned, and then detect wrapping (and hence carry). What is very annoying, is that this is masses of conditional logic and multiple instructions to implement "add with carry", which is an instruction in every instruction set I have ever used. (Note: it may be worth making the final calculation of carry_out use | rather than || - it saves branching, which is bad for performance. As always, profile to see if it helps.)

If you are going to eventually support multiplication, you need a type which is twice as wide as your "digit" - in which case, you might as well use it for addition too. Using the variables from above, and assuming "unsigned long long" is your "wide" type:

    const auto temp = (unsigned long long)lhs + rhs + carry_in;
    sum = temp; // Will truncate.
    carry_out = temp > UINT_MAX;

Choosing your "wide" type can be tricky. As a first pass, it's probably best to use uint32_t for your digits and uint64_t for your wide type.

For more details on implementing multi-precision arithmetic, Chapter 14 from Handbook of Applied Cryptography looks useful.



来源:https://stackoverflow.com/questions/41077514/big-integer-addition-i-know-the-theory-still-rusty-in-practice

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!