如何将BigInteger转换为字符串

How to convert Biginteger to string

本文关键字:字符串 转换 BigInteger      更新时间:2023-10-16

i具有一个数字数字的向量,向量代表基本2^32的系统中的大整数。例如:

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

此向量表示这个大整数:

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

如何在字符串中获得此小数点表示?

这是一种做您想做的事情的低效方法,从代表任意大小的整数的单词值的向量中获取小数字符串。

我宁愿将其作为类实现,以更好地封装,因此可以添加数学运算符,但是为了更好地遵守问题,这只是用于操纵std::vector<unsigned>对象的一堆免费功能。这确实使用Typedef BiType作为std::vector<unsigned>的别名。

进行二进制部门的功能构成了大部分此代码。它的大部分内容复制了std::bitset可以做的事情,但对于任意大小的位,作为unsigned单词的向量。如果您想提高效率,请插入执行人均操作而不是每位操作的部门算法。此外,当仅用于除以10的情况时,分区代码是通用的,因此您可以用特殊用途的分区代码替换。

代码通常假定unsigned单词的向量,并且基础是最大unsigned值,另外一个值。我在较小的基地或不是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