快速将二进制数转换为十进制数的方法

12

我需要尽快将类似于unsigned int bin_number = 10101010的二进制数转换为其十进制表示(即170)?最好的算法是什么?


1
10101010 是来自于你程序的用户,还是只是代码中的字面量? - Michael Kristofik
你能更清晰地解释一下二进制数是从哪里来的吗?它是在编译时已知还是只有在运行时才确定?它是存储在字符串中还是其他结构中?知道这些将使回答问题变得更容易。 - Component 10
是的,很抱歉。通常我在运行时获取数字,但有时在编译时获取。我还在学习中。 - Nick
5个回答

14

使用模板可以在编译时解决这个问题。

template<unsigned long num>
struct binary
{
    static unsigned const value =
        binary<num/10>::value << 1 | num % 10;
};

// Specialization for zero
template<>
struct binary<0>
{ static unsigned const value = 0; };

使用较小的num再次实例化二进制模板,直到num减少到零并以特化作为终止条件。

例如: std::cout << binary<10101010>::value;

对于运行时问题:

unsigned binary_to_decimal(unsigned num)
{
    unsigned res = 0;

    for(int i = 0; num > 0; ++i)
    {
        if((num % 10) == 1)
            res += (1 << i);

        num /= 10;
    }

    return res;
}

6
使用模板可以在编译时计算任何内容,因为它们是图灵完备的。然而,这是否有助于OP完成任何工作呢? - PlasmaHH
1
我怀疑这个。如果 OP 有一个 ICE,他需要使用元编程,他可以只做 const double d = 170.0;。由于他肯定在运行时获取入站号码,因此元编程已经不再适用了。 - John Dibling
4
实际上,我认为这相当聪明——更重要的是它确实有效。确实,它无法处理仅在运行时已知的数字,并且真正有可能OP想要那样做,但是他没有指定。 - Component 10
2
@gliderkite:没错。 :) 对于那些不了解元编程的人来说,对其进行详细说明是有教育意义的,即使可能不适用于实际问题。更重要的是,您提供了可能是实际问题的解决方案。虽然我不知道它是否是最快的算法,但肯定足够快,而且 OP 没有明确说明“最佳”是什么意思。我已经取消了我的踩和给了你一个 +1。 - John Dibling
在编译时,您不需要模板,只需编写 int i = 0b10101010; 即可,这是标准的:http://eel.is/c++draft/lex#nt:binary-literal - Martin Morterol

11

如果这个"number"实际上是从某些来源获取的字符串(从文件或用户输入),你将其转换为数字(认为它更适合成为实际数字),这很可能会发生,那么你可以使用std :: bitset进行转换:

#include <bitset>

unsigned int number = std::bitset<32>("10101010").to_ulong();
当然,这里的 32 是由实现定义的,可能更适合写成 std::numeric_limits<unsigned int>::digits
但如果它在(非常)开始时真的是一个数字(整数变量),则可以执行以下操作:
#include <string>

unsigned int number = std::bitset<32>(std::to_string(bin_number)).to_ulong();

(使用C++11的to_string)但这可能不再是最有效的方法,因为其他人提出了基于数字的更有效的算法。但如上所述,我怀疑您是否真的将此数字作为实际整数变量首先获得,而是从某些文本文件或用户那里读取。


1
谢谢,这是一个好答案,但是这个数字不是字符串,我不能使用C++11,并且我要求一个快速的解决方案! - Nick
@Nick,我可以问一下你从哪里得到它的吗?显然你必须从某个地方获取它,我怀疑你实际上并没有读取一个只包含0和1的二进制数,那样是无意义的。首先,只有从某种文本媒介中获取这样的数字才有意义。唯一的例外是当你需要二进制常量时,但是对于这种情况,你可以使用另一种方法(gliderkite的模板程序非常好)。但是对于大多数情况,当它作为字符串出现时,bitset解决方案不应该是最慢的(而且它也不需要C++11)。 - Christian Rau

4

实际上,如果你写unsigned int bin_number = 10101010,编译器会将其解释为一个十进制数。

如果你想在源代码中写一个二进制字面量,应该使用BOOST_BINARY。然后,你只需要使用cout打印它,默认情况下是十进制...

unsigned int i = BOOST_BINARY(10101010);
std::cout << i; // This prints 170

一些编译器也支持“0b”前缀(i = 0b10101010),但使用boost可以确保可移植性。 - Aurel
@Nick 请访问http://www.boost.org/doc/libs/1_39_0/libs/utility/utility.htm#BOOST_BINARY。 - SRN
@Nick,请查看以下视频链接: http://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-Stephan-T-Lavavej-Advanced-STL-5-of-n - SRN
4
只要使用得当,Boost库是非常可靠的(如果你从未使用过,你应该去看看!) - Aurel
5
@Nick 是和不是。在这种情况下,已经采取了必要的预防措施:使用前缀避免名称冲突,并以全大写形式表示它是一个宏,这样您就知道可以采取适当的预防措施。 - James Kanze
显示剩余2条评论

1

自C++11以来(即使在这方面C++11比C++14更有限),函数可以是constexpr,因此避免了需要template来具有编译时值的必要性。

以下是与C++14兼容的版本:

constexpr unsigned binary_to_decimal(unsigned num)
{
    unsigned res = 0;

    while (num)
    {
        res = 10 * res + num % 10;
        num /= 10;
    }
    return res;
}

对于字面量,自C++14起,您甚至可以使用二进制字面量:

0b1010'1010 // or 0b10101010 without separator

0

如果您知道您正在处理的二进制位数,而且它始终是固定的,并且二进制数字以字符串形式出现(如果从文件或stdin读取,则会这样),则可以采用以下方法:

int to_binary( const char* c )
{
    return ( ( c[0] & 1 ) ? 0x80 : 0x00 ) |
           ( ( c[1] & 1 ) ? 0x40 : 0x00 ) |
           ( ( c[2] & 1 ) ? 0x20 : 0x00 ) |
           ( ( c[3] & 1 ) ? 0x10 : 0x00 ) |
           ( ( c[4] & 1 ) ? 0x08 : 0x00 ) |
           ( ( c[5] & 1 ) ? 0x04 : 0x00 ) |
           ( ( c[6] & 1 ) ? 0x02 : 0x00 ) |
           ( ( c[7] & 1 ) ? 0x01 : 0x00 );
}

这假设有一个固定的八位二进制数,被称为:

std::cout << to_binary("10101010") << std::endl;

如果你有一个十六位的数字,你仍然可以使用它:

const char* bin_number = "1010101010101010";

// Deal with 16 bits
std::cout << ( to_binary( bin_number ) << 8 | to_binary( bin_number + 8 ) ) << std::endl;

请注意,此处显然没有边界检查,我依赖于“1”的最低有效位始终为1,“0”始终为0的事实(因此不验证它是否实际上是二进制输入)。
当然,这很具体而且不太灵活,但它完成了工作,我不确定你能得到更快的速度。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接