1

I have a vector with digits of number, vector represents big integer in system with base 2^32. For example:

vector <unsigned> vec = {453860625, 469837947, 3503557200, 40}

This vector represent this big integer:

base = 2 ^ 32
3233755723588593872632005090577 = 40 * base ^ 3 + 3503557200 * base ^ 2 + 469837947 * base + 453860625

How to get this decimal representation in string?

Andrii Matiiash
  • 499
  • 6
  • 18
  • 4
    Efficiently? With difficulty. – DeiDei Nov 30 '16 at 22:01
  • you can reference the way they do here [How to print __int128 in g++?](http://stackoverflow.com/q/25114597/995714), [how to print __uint128_t number using gcc?](http://stackoverflow.com/q/11656241/995714) – phuclv Dec 01 '16 at 03:06

1 Answers1

2

Here is an inefficient way to do what you want, get a decimal string from a vector of word values representing an integer of arbitrary size.

I would have preferred to implement this as a class, for better encapsulation and so math operators could be added, but to better comply with the question, this is just a bunch of free functions for manipulating std::vector<unsigned> objects. This does use a typedef BiType as an alias for std::vector<unsigned> however.

Functions for doing the binary division make up most of this code. Much of it duplicates what can be done with std::bitset, but for bitsets of arbitrary size, as vectors of unsigned words. If you want to improve efficiency, plug in a division algorithm which does per-word operations, instead of per-bit. Also, the division code is general-purpose, when it is only ever used to divide by 10, so you could replace it with special-purpose division code.

The code generally assumes a vector of unsigned words and also that the base is the maximum unsigned value, plus one. I left a comment wherever things would go wrong for smaller bases or bases which are not a power of 2 (binary division requires base to be a power of 2).

Also, I only tested for 1 case, the one you gave in the OP -- and this is new, unverified code, so you might want to do some more testing. If you find a problem case, I'll be happy to fix the bug here.

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

namespace bigint {
using BiType = std::vector<unsigned>;

// cmp compares a with b, returning 1:a>b, 0:a==b, -1:a<b
int cmp(const BiType& a, const BiType& b) {
    const auto max_size = std::max(a.size(), b.size());
    for(auto i=max_size-1; i+1; --i) {
        const auto wa = i < a.size() ? a[i] : 0;
        const auto wb = i < b.size() ? b[i] : 0;
        if(wa != wb) { return wa > wb ? 1 : -1; }
    }
    return 0;
}

bool is_zero(BiType& bi) {
    for(auto w : bi) { if(w) return false; }
    return true;
}

// canonize removes leading zero words
void canonize(BiType& bi) {
    const auto size = bi.size();
    if(!size || bi[size-1]) return;
    for(auto i=size-2; i+1; --i) {
        if(bi[i]) {
            bi.resize(i + 1);
            return;
        }
    }
    bi.clear();
}

// subfrom subtracts b from a, modifying a
// a >= b must be guaranteed by caller
void subfrom(BiType& a, const BiType& b) {
    unsigned borrow = 0;
    for(std::size_t i=0; i<b.size(); ++i) {
        if(b[i] || borrow) {
            // TODO: handle error if i >= a.size()
            const auto w = a[i] - b[i] - borrow;
            // this relies on the automatic w = w (mod base),
            // assuming unsigned max is base-1
            // if this is not the case, w must be set to w % base here
            borrow = w >= a[i];
            a[i] = w;
        }
    }
    for(auto i=b.size(); borrow; ++i) {
        // TODO: handle error if i >= a.size()
        borrow = !a[i];
        --a[i];
        // a[i] must be set modulo base here too
        // (this is automatic when base is unsigned max + 1)
    }
}

// binary division and its helpers: these require base to be a power of 2
// hi_bit_set is base/2
// the definition assumes CHAR_BIT == 8
const auto hi_bit_set = unsigned(1) << (sizeof(unsigned) * 8 - 1);  

// shift_right_1 divides bi by 2, truncating any fraction
void shift_right_1(BiType& bi) {
    unsigned carry = 0;
    for(auto i=bi.size()-1; i+1; --i) {
        const auto next_carry = (bi[i] & 1) ? hi_bit_set : 0;
        bi[i] >>= 1;
        bi[i] |= carry;
        carry = next_carry;
    }
    // if carry is nonzero here, 1/2 was truncated from the result
    canonize(bi);
}

// shift_left_1 multiplies bi by 2
void shift_left_1(BiType& bi) {
    unsigned carry = 0;
    for(std::size_t i=0; i<bi.size(); ++i) {
        const unsigned next_carry = !!(bi[i] & hi_bit_set);
        bi[i] <<= 1; // assumes high bit is lost, i.e. base is unsigned max + 1
        bi[i] |= carry;
        carry = next_carry;
    }
    if(carry) { bi.push_back(1); }
}

// sets an indexed bit in bi, growing the vector when required
void set_bit_at(BiType& bi, std::size_t index, bool set=true) {
    std::size_t widx = index / (sizeof(unsigned) * 8);
    std::size_t bidx = index % (sizeof(unsigned) * 8);
    if(bi.size() < widx + 1) { bi.resize(widx + 1); }
    if(set) { bi[widx] |= unsigned(1) << bidx; }
    else    { bi[widx] &= ~(unsigned(1) << bidx); }
}

// divide divides n by d, returning the result and leaving the remainder in n
// this is implemented using binary division
BiType divide(BiType& n, BiType d) {
    if(is_zero(d)) {
        // TODO: handle divide by zero
        return {};
    }
    std::size_t shift = 0;
    while(cmp(n, d) == 1) { 
        shift_left_1(d);
        ++shift;
    }
    BiType result;
    do {
        if(cmp(n, d) >= 0) {
            set_bit_at(result, shift);
            subfrom(n, d);
        }
        shift_right_1(d);
    } while(shift--);
    canonize(result);
    canonize(n);
    return result;
}

std::string get_decimal(BiType bi) {
    std::string dec_string;

    // repeat division by 10, using the remainder as a decimal digit
    // this will build a string with digits in reverse order, so
    // before returning, it will be reversed to correct this.
    do {
        const auto next_bi = divide(bi, {10});
        const char digit_value = static_cast<char>(bi.size() ? bi[0] : 0);
        dec_string.push_back('0' + digit_value);
        bi = next_bi;
    } while(!is_zero(bi));
    std::reverse(dec_string.begin(), dec_string.end());
    return dec_string;
}

}

int main() {
    bigint::BiType my_big_int = {453860625, 469837947, 3503557200, 40};
    auto dec_string = bigint::get_decimal(my_big_int);
    std::cout << dec_string << '\n';
}

Output:

3233755723588593872632005090577
Christopher Oicles
  • 3,017
  • 16
  • 11