2016-11-30 46 views
1

我有一個帶有數字位數的向量,向量表示帶有基數2^32的系統中的大整數。例如:如何將Biginteger轉換爲字符串

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

這個矢量代表這個大整數

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

如何得到這個字符串十進制表示?

+4

高效?有困難。 – DeiDei

+0

你可以參考他們在這裏的做法[如何在g ++中打印__int128?](http://stackoverflow.com/q/25114597/995714),[如何使用gcc打印__uint128_t數字?](http:// stackoverflow .com/q/11656241/995714) –

回答

2

這是一種低效率的方式來做你想做的,從一個表示任意大小的整數的字值向量中獲得一個十進制字符串。

我更喜歡將它作爲一個類來實現,爲了更好的封裝,所以可以添加數學運算符,但爲了更好地遵守這個問題,這只是一堆用於操縱對象的自由函數。但是,這確實使用了typedef BiType作爲std::vector<unsigned>的別名。

執行二進制除法的功能構成了大部分代碼。它的大部分重複可以用std::bitset做什麼,但是對於任意大小的位集,作爲unsigned單詞的向量。如果您想要提高效率,請插入一種除字節操作而不是每位操作的除法算法。另外,分頻碼是通用的,當它只用於10分頻時,所以你可以用專用分頻碼代替它。

該代碼通常假設矢量爲unsigned個字,並且基數最大值爲unsigned,再加上一個。對於不是2的冪的較小基數或基數(二進制分數需要基數爲2的冪),我會留下評論。另外,我只測試了1個案例,你在OP中給出的 - 這是新的,未經驗證的代碼,所以你可能想要做更多的測試。如果您發現問題案例,我很樂意解決這個問題。

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

輸出:

3233755723588593872632005090577 
+0

哇! BigInteger的一些數學運算符的直接實現。很好。 –