【问题标题】:How to print the decimal value of a `std::bitset` bigger than a `unsigned long long`?如何打印大于“unsigned long long”的“std::bitset”的十进制值?
【发布时间】:2020-05-14 09:46:21
【问题描述】:

我正在编写一个类来处理带有更多位 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) 的方式,因为我无法存储结果变成一个变量,我想尽可能少地使用外部库。 有没有办法使用按位运算符或任何其他方式进行转换?

【问题讨论】:

  • 欢迎来到 Stack Overflow。请花点时间浏览The Tour 并参考Help Center 的材料,了解您可以在这里询问什么以及如何询问。发布minimal reproducible example 特别重要。
  • 我们只能咨询水晶球,了解您的 “比 long long int 更大的新 int 类型。” 是什么——而现在不是工作得很好。请提供A Minimal, Complete, and Verifiable Example (MCVE)
  • 您是否看过任何大整数实现(例如,Boost's Integer Types)?
  • 不,我想创建自己的整数。这更像是一种锻炼,而不是我工作中真正需要的东西
  • 我相信您的问题可以使用更好的标题和更好的介绍(摆脱介绍您的大整数实现)。例如:“如何打印由std::bitset 表示的、太大而无法放入unsigned long long 的十进制值?”。不过,我认为这是一个很好的问题。

标签: c++ converters


【解决方案1】:

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

这个示例类应该会有所帮助。它不是传统的 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'); });
}

使用这个类应该很容易从位集构建 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

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

-----------------cmets后更新----------

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

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();
}

【讨论】:

  • 我查阅了 BCD 以及它们的工作原理,谢谢。我不知道这种写十进制数的方法,但我还是想找到一种方法来转换bitset
  • 这根本不能回答问题。将大整数存储为数字向量使得输出变得微不足道。重要的是(开始这个问题的主题)是如何将任意大的二进制表示转换为其十进制对应物。
【解决方案2】:

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

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 的底部删除与0s i 一样多的数字。例如:12345 / 100 = 123。 之后的操作正好相反:它保留与0s 一样多的数字,而不是删除它们。 这个循环然后打印一个数字并在之后立即将其删除,传递并打印它们。

【讨论】:

  • "第一行复制数字以避免值发生变化。" - 在这种情况下,最好按值传递,即 RossInt value 而不是 @987654337 @ 并避免在函数内部复制它。
猜你喜欢
  • 2012-09-11
  • 2013-10-17
  • 2015-06-13
  • 2020-07-26
  • 2013-03-05
  • 2019-11-24
  • 2011-03-13
  • 1970-01-01
相关资源
最近更新 更多