如何打印大于 unsigned long long 的 std::bitset 的十进制值?

How to print the decimal value of a `std::bitset` bigger than a `unsigned long long`?

我正在编写一个 class 来处理整数,其位集包含 unsigned long long 的更多位。

#include <bitset>
#include <string>

#define MAX_BITS 32000

class RossInt {
    std::bitset<MAX_BITS> bits;

public:
    RossInt(unsigned long long num); /* Copies the bits of num into the bitset */
    RossInt operator+ (const RossInt& op) const;

    std::string to_string() const; /* Returns the number in decimal in a string */
};

因为数字大于 unsigned long long 我会把它放在一个字符串中,但问题是我不能使用我通常使用 decimal_n += pow(2, bit_index) 的方式我无法将结果存储到变量中,我想尽可能少地使用外部库。 有没有办法使用按位运算符或任何其他方式转换它?

您正在寻找的是二进制->BCD(二进制编码的十进制)解决方案。一旦你有了 BCD,打印它就很容易了。 鉴于 BCD 数学被大量使用,我尝试寻找标准解决方案,但找不到。

这个例子 class 应该有所帮助。它不是传统的 BCD 表示法,但更容易理解。

class BCD
{
public:
    // function to make the "root" value.
    static BCD One()     { BCD retval; retval.digits = {1}; return retval; }
    // we will generate all powers of two from doubling from one
    void Double()
    {
        for(auto& d : digits) d *= 2;
        Normalise();
    }
    // We can generate all other numbers by combining powers of two.
    void operator+=(const BCD& other)
    {
        if (other.digits.size()>digits.size())
            digits.resize(other.digits.size(), 0);
        std::transform(digits.begin(), digits.end(), other.digits.begin(), digits.begin(), std::plus<char>{});    
        Normalise();
    }
    friend inline std::ostream& operator << (std::ostream&, const BCD&);
private:
        // "normal form" is all digits 0-9, if required carry the one 
        void Normalise()
        {
            int carry = 0;
            for(auto& d : digits) {
                d+=carry;
                carry = d/10;
                d = d%10;
            }
            if (carry) digits.push_back(carry);
        }
        std::vector<char> digits;
};

inline std::ostream& operator<<(std::ostream& os, const BCD& bcd)
{
    for_each(bcd.digits.rbegin(), bcd.digits.rend(), [&os](char d){ os << (char)(d+'0'); });
}

使用这个 class 从位集构建 BCD 应该很简单。例如

int main()
{
    BCD power_of_two = BCD::One();
    BCD total;
    for (int i = 0; i < 10; ++i) {
        total += power_of_two;
        std::cout << power_of_two <<  " total = " << total << "\n";
        power_of_two.Double();
    }
    return 0;
}

产生

1 total = 1
2 total = 3
4 total = 7
8 total = 15
16 total = 31
32 total = 63
64 total = 127
128 total = 255
256 total = 511
512 total = 1023

您所要做的就是使“+=”以集合中的位为条件。我并不是说这是最好的方法,但它应该足以测试您的结果。
希望对你有帮助

----------------评论后更新----------

有人指出我的解决方案不是完整的解决方案。 本着乐于助人的精神,我将提供如何使用上面的 BCD class 构建 "to_string" 函数。
如果你不明白这一点,你必须阅读 number theory。你必须这样做,因为 10=5*2。 5 是奇数,因此不存在 10^x = 2^y,其中 x 和 y 是整数。实际上,这意味着十进制表示中的第一个数字取决于二进制表示中的每个数字。您必须通过将两个的所有幂相加来确定它。您也可以使用相同的逻辑生成整数。
请注意,堆栈溢出的存在是为了帮助陷入困境的程序员,而不是让其他人为他们工作。请不要指望有其他人 post 为您编写程序。

std::string to_string() const
{
    BCD power_of_two = BCD::One();
    BCD total;
    for (int idx = 0; idx < bits.size(); ++idx) {
        if (bits.test(idx))
            total += power_of_two;
        power_of_two.Double();
    }
    std::ostringstream os;
    os << total;
    return os.str();
}

在它上面浪费了两个晚上之后,我终于找到了打印它的方法。这不是最有效的方法,我会重写它,直到我对速度感到满意为止。

std::ostream& operator<<(std::ostream& os, const RossInt& value) {
    RossInt op = value;
    RossInt i = 1;
    while (op / i > 0) i = i * 10;

    do {
        i = i / 10;
        os << (op / i).to_ullong();
        op = op % i;
    } while (i > 1);

    return os;
}

这段代码中使用的逻辑非常简单:如果 x = y 并且我将它们都除以 10^x,x 仍然等于 y。使用这个 属性 我写了一个代码来逐位打印数字。 第一行复制数字以避免值发生变化。

    RossInt i = 1;
    while (op / i > 0) i = i * 10;

i的初始值为1,乘法的中性数。在 while 中,它不断增加 i 直到它大于数字的值,这样就可以知道数字的位数而无需将其转换为十进制。

    do {
        i = i / 10;
        os << (op / i).to_ullong();
        op = op % i;
    } while (i > 1);

这个循环是用来真正打印数字的。 在每次迭代中,i 都会松掉一个 0。将 op 除以 i 会从 op 的底部删除与 0 一样多的数字 i 已。例如:12345 / 100 = 123。 之后的操作恰恰相反:它保留与 0 一样多的数字而不是删除它们。 这个循环然后打印一个数字并在之后立即将其删除,传递并打印它们。