将整数转换为二进制数字字符串

40

我目前正在为计算机体系结构课程编写 MIPS 处理器的 C++ 模拟程序,但在将十进制数转换为二进制数时遇到了一些问题(包括有符号数)。一切都很顺利,直到最后一位,因为我的当前算法超出了 int 上 1<<=31 的范围。只需要一个指引来帮助我解决这个问题。谢谢!

//Assume 32 bit decimal number
string DecimalToBinaryString(int a)
{
    string binary = "";
    int mask = 1;
    for(int i = 0; i < 31; i++)
    {
        if((mask&a) >= 1)
            binary = "1"+binary;
        else
            binary = "0"+binary;
        mask<<=1;
    }
    cout<<binary<<endl;
    return binary;
}

为了完整性,我也包含了我的另一个算法。对于缺乏注释的情况,我向您道歉,不过这个算法相当简单。

int BinaryStringToDecimal(string a)
{
    int num = 0;
    bool neg = false;
    if(a.at(0) == '1')
    {
        neg = true;
        for(int x = a.length()-1; x >= 0; x--)
        {
            if(a.at(x) == '1')
                a.at(x) = '0';
            else a.at(x) = '1';
        }
        a.at(a.length()-1) += 1;
        for(int x = a.length()-1; x >= 0; x--)
        {
            if(a.at(x) == '2')
            {
                if(x-1 >= 0)
                {
                    if(a.at(x-1) == '1')
                        a.at(x-1) = '2';
                    if(a.at(x-1) == '0')
                        a.at(x-1) = '1';
                    a.at(x) = '0';
                }
            }
            else if(a.at(x) == '3')
            {
                if(x-1 >= 0)
                    a.at(x-1) += '2';
                a.at(x) = '1';
            }
        }
        if(a.at(0) == '2')
            a.at(0) = '0';
        else if(a.at(0) == '3')
            a.at(0) = '1';
    }
    for(int x = a.length()-1; x >= 0; x--)
    {
        if(a.at(x) == '1')
            num += pow(2.0, a.length()-x-1);
    }
    if(neg)
        num = num*-1;   
    return num;
 }

如果有人知道更好的方法来更高效地编写这些代码,我会很乐意听取建议。我只上过两个入门级的编程课程,但一直在尝试不同的技术,以了解它们的风格和效果如何。


改善效率的一个选项是每次处理4位,将每个nybble映射到相应的4字符字符串。这样可以减少字符串赋值操作。我还会考虑以MSB到LSB的顺序生成答案,而不是反过来,这样你就可以使用binary += pattern[nybble];。在右侧扩展字符串可能比你现在的左侧插入方式更有效(几乎无法测量)。 - Jonathan Leffler
除非我真的需要自己进行转换,否则我会使用 std::bitset 来处理转换和打印:std::cout << std::bitset<32>(a); - Jerry Coffin
除了像@JonathanLeffler所说的从左到右工作之外,我建议您预先分配整个字符串,因为您知道输出将是32个字符。这可能会根据您的字符串实现有所不同,但肯定不会有害。 - Pablo
5个回答

107

实际上有标准的一行代码可以解决这些问题。

#include <bitset>

std::string s = std::bitset< 64 >( 12345 ).to_string(); // string conversion

std::cout << std::bitset< 64 >( 54321 ) << ' '; // direct output

std::bitset< 64 > input;
std::cin >> input;
unsigned long ul = input.to_ulong();

点击此链接查看演示.


2
@Chaos_99 这太过陈旧了。你从哪里找到的? - Potatoswatter
1
@Chaos_99 很久以前,没有人能够制作出可靠的编译器,也没有足够的用户和测试用例来进行充分的预发布质量保证。那些日子已经过去了,但有些人选择留在那个时代。 - Potatoswatter
1
不要告诉我,我宁愿尽早切换。但是如果你必须保证你的代码在20多年内编译完全相同,你不能只是偶尔进行一次apt-get upgrade。但是让我们在这里结束吧。这对其他人没有帮助。 - Chaos_99
5
令人惊讶的是,似乎没有人提到 bitset 的 to_string 函数将返回填充后的二进制表示。很明显,由于 bitset 返回的是集合的二进制表示,所以它应该返回这个。但是,如果有人在“误用” bitset 将 int 转换为二进制字符串表示形式,则会调用输出并填充结果。cout << bitset<16>(21); // 输出 0000000000010101 - Vlatko Šurlan
1
@parasrish 0x 表示十六进制,而不是二进制。C++14 引入了以 0b 开头的二进制字面量。 - Potatoswatter
显示剩余11条评论

5

替换:

if((mask&a) >= 1)

使用以下任一个:

if ((mask & a) != 0)

或者:

if (mask & a)

你的问题在于最后一位给出的是一个负数而不是正数。


2
我检查了你的代码,没有发现任何错误。这是我使用的代码...
#include <iostream>
#include <string>
using namespace std;

int main ()
{
  int a=1111165117;
  string binary  ("");
    int mask = 1;
    for(int i = 0; i < 31; i++)
    {
    if((mask&a) >= 1)
        binary = "1"+binary;
    else
        binary = "0"+binary;
     mask<<=1;
 }
 cout<<binary<<endl;
 system("PAUSE");         //optional if not using ideone
 return EXIT_SUCCESS;     //optional if not using ideone
 }

输出将为1001110001010110101111010011101。 您可以在ideone上运行此代码。

0

关于1<<=31的问题已在其他评论中解决。关于字符串 -> 整数转换的代码片段,您有几个选项:

  • 将字符串转换为流并使用为流定义的operator>>(int&) //更正 - 没关系,setbase()流修饰符不支持2个值作为参数
  • 使用具有基值参数(二进制为2)的标准C函数strtol()
  • 或者如果您真的想要自己实现转换,请尝试以下代码:

    int BinaryStringToDecimal(string a) 
    {
        int Rslt = 0;
        int Mask = 1;
        for (int i = a.length()-1; i >= 0; --i, Mask <<= 1) {
            if (a.at(i) != '0') {
                Rslt |= Mask;
            }
        }
        return (Rslt);
    }
    
请注意,此代码在处理负数时与您的代码不同:在您的函数中,最高位被视为符号位。如果您的函数的字符串参数中最左边的位不在第32位(从右边开始计数),则您的函数可能会产生错误的结果。在此处建议的代码中,没有特殊的符号处理。但是,如果您得到了32个数字的字符串,并且它们的最左边是'1',则int结果中的MSB将等于1,并且整数将为负数(应该是这样)。

0

为什么不能将 int 强制转换为 uint?这样,生成二进制字符串就很容易了,因为您不必担心符号位。同样适用于将二进制字符串转换为 int:先构建为 uint,然后将其强制转换为 int

string DecimalToBinaryString(int a)
{
    uint b = (uint)a;
    string binary = "";
    uint mask = 0x80000000u;
    while (mask > 0)
    {
        binary += ((b & mask) == 0) ? '0' : '1';
        mask >>= 1;
    }
    cout<<binary<<endl;
    return binary;
}

当然,您可以应用提到的优化,如预分配字符串缓冲区等。

反过来:

uint b = 0;
for (int i = 31; i >=0; --i)
{
    b <<= 1;
    if (a.at(i) == '1')
        b |= 1;
}
int num = (int)b;

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