如何使用cout打印二进制格式的数字?

308

我正在学习一门关于操作系统的大学课程,我们正在学习如何将二进制转换为十六进制、十进制转换为十六进制等,今天我们刚刚学习了如何使用二进制补码(~number + 1)在内存中存储带符号/无符号数。

我们有几个练习题需要手写计算,但在提交作业给老师之前我想要验证我的答案是否正确。我已经用C++编写了前几个问题的程序,但现在卡在如何验证以下问题的答案上:

char a, b;

short c;
a = -58;
c = -315;

b = a >> 3;

我们需要显示abc在内存中的二进制表示。

我已经手写计算了它们的二进制补码,并得到以下结果:

a = 00111010(它是一个字符,所以1个字节)

b = 00001000(它是一个字符,所以1个字节)

c = 11111110 11000101(它是一个短整型,所以2个字节)

有没有一种方法可以验证我的答案?在C++中是否有标准的方法来显示数字在内存中的二进制表示,还是我必须自己编写每个步骤的代码(计算二进制补码,然后转换为二进制)?我知道后者不会花费太长时间,但我想知道是否有一种标准的方式来这样做。


3
你了解十六进制表示吗?如果了解,你可以打印十六进制表示(使用“std::hex”)操作符 - 我会让你练习处理其余的内容... - Nim
4
你很强调“内存”这个词,但我希望他们没有让你去处理字节序问题。 - Mark Ransom
1
你知道字节序是什么吗?如果知道,那么对于这个练习你是否关心它呢?这些问题的答案可能会影响你的问题的答案。 - R. Martinho Fernandes
3
就像这样,即使是 Google 也会这样做,例如 “-58 in binary”。但是想要自己在代码中找到如何实现的话,可以给予加1赞扬。 - Konrad Rudolph
即使只是一瞥,所有这些数字看起来都是错误的。 -58 是负数,所以我立刻知道第一个位应该是 1,但你的答案第一个位是 0。而且 00111010 右移 3 位是 000 00111,没有任何数学运算,所以那个答案也是错误的。 - Mooing Duck
显示剩余2条评论
13个回答

569

最简单的方法可能是创建一个代表该值的std::bitset,然后将其流式传输到cout

#include <bitset>
...

char a = -58;
std::bitset<8> x(a);
std::cout << x << '\n';

short c = -315;
std::bitset<16> y(c);
std::cout << y << '\n';

2
请原谅我的无知,这只会显示一个数字的二进制表示(例如,8将是00001000),还是它的内存表示(例如,如何通过处理符号位并使用“二补数”来存储-8)? - Jesse Emond
12
bitsetзҡ„жһ„йҖ еҮҪж•°еҸӮж•°иў«и§ЈйҮҠдёәж— з¬ҰеҸ·еҖјпјҢдёҺдәҢиҝӣеҲ¶иЎҘз ҒзӣёеҗҢгҖӮдёҘж јжқҘиҜҙпјҢC++дёҚдҝқиҜҒдҪҝз”ЁдәҢиҝӣеҲ¶иЎҘз ҒиҝӣиЎҢз®—жңҜиҝҗз®—пјҢиҖҢдё”дҪ зӨәдҫӢдёӯзҡ„-58 >> 3ж“ҚдҪңжҳҜжңӘе®ҡд№үзҡ„гҖӮ - Potatoswatter
我能将bitset值(例如此示例中的x或y)强制转换为char *吗? - nirvanaswap
1
@nirvanaswap: 我想你可以进行类型转换,但是结果可能不是很有用。如果你需要将结果作为字符串使用,请使用 bitsetto_string 成员函数。 - Jerry Coffin
1
谢谢Jerry,我在几分钟后发现了to_string。顺便说一下,强制转换不起作用,bitset变量是某个看起来非常神秘的bitset3ul(?!)类的对象。最好让抽象化去完成工作! - nirvanaswap
显示剩余4条评论

161

使用即时转换到 std::bitset。无临时变量,无循环,无函数,无宏定义。

在 Coliru 上实时运行

#include <iostream>
#include <bitset>

int main() {
    int a = -58, b = a>>3, c = -315;

    std::cout << "a = " << std::bitset<8>(a)  << std::endl;
    std::cout << "b = " << std::bitset<8>(b)  << std::endl;
    std::cout << "c = " << std::bitset<16>(c) << std::endl;
}

输出:

a = 11000110
b = 11111000
c = 1111111011000101

30
请注意,硬编码大小并非必要。例如,要打印“x”,可以使用以下代码:std::cout << std::bitset<8*sizeof(x)>(x) - Apollys supports Monica

67
在C++20中,您可以使用std::format来实现这一点。
unsigned char a = -58;
std::cout << std::format("{:b}", a);

输出:

11000110

在旧系统中,您可以使用 {fmt} 库,这是基于std::format的。 {fmt} 还提供了print函数,使这更加简单和高效(godbolt):

unsigned char a = -58;
fmt::print("{:b}", a);

声明:我是 {fmt} 和 C++20 的 std::format 的作者。


“std::format” 在 Visual Studio 2019 中还没有可用,对吗? - rturrado
3
@rturrado std::format 在 Visual Studio 2019 中可用。您需要 #include <format> 并设置 /std:c++latest - Chris G.
@ChrisG。感谢您提供的信息! - rturrado

31

如果你想显示任何对象的位表示,而不仅仅是整数,请记得先将其重新解释为 char 数组,然后可以打印该数组的内容,以十六进制或甚至二进制(通过 bitset)的形式:

#include <iostream>
#include <bitset>
#include <climits>

template<typename T>
void show_binrep(const T& a)
{
    const char* beg = reinterpret_cast<const char*>(&a);
    const char* end = beg + sizeof(a);
    while(beg != end)
        std::cout << std::bitset<CHAR_BIT>(*beg++) << ' ';
    std::cout << '\n';
}
int main()
{
    char a, b;
    short c;
    a = -58;
    c = -315;
    b = a >> 3;
    show_binrep(a);
    show_binrep(b);
    show_binrep(c);
    float f = 3.14;
    show_binrep(f);
}
请注意,大多数常见系统都是小端字节序,因此show_binrep(c)的输出是您期望的1111111 011000101,因为这不是它在内存中的存储方式。如果您正在寻找二进制中的表示,则可以使用简单的cout << bitset<16>(c)

13
在C++中,是否有标准的方法来显示数字的内存二进制表示方式?没有。就像std :: hex或std :: dec一样,没有std :: bin,但自己输出数字的二进制表示并不难:您可以通过掩码左移所有其他位来输出最左边的位,并为您拥有的所有位重复此过程。(类型中的位数为sizeof(T)*CHAR_BIT)。

5
与已发布的内容类似,只需使用位移和掩码来获取位; 可用于任何类型,是一个模板。
#include<iostream>
#include <climits>

template<typename T>
void printBin(const T& t){
    size_t nBytes=sizeof(T);
    char* rawPtr((char*)(&t));
    for(size_t byte=0; byte<nBytes; byte++){
        for(size_t bit=0; bit<CHAR_BIT; bit++){
            std::cout<<(((rawPtr[byte])>>bit)&1);
        }
    }
    std::cout<<std::endl;
};

int main(void){
    for(int i=0; i<50; i++){
        std::cout<<i<<": ";
        printBin(i);
    }
}

5
获取每个字节中比特数的标准方法是宏CHAR_BIT - R. Martinho Fernandes
看起来sbi根据@R.MartinhoFernandes的评论编辑了Εύδοξος的帖子。然而,他没有改变最后一句话。我会进行编辑。 - gsamaras

4

可重复使用的函数:

template<typename T>
static std::string toBinaryString(const T& x)
{
    std::stringstream ss;
    ss << std::bitset<sizeof(T) * 8>(x);
    return ss.str();
}

用法:

int main(){
  uint16_t x=8;
  std::cout << toBinaryString(x);
}

这适用于各种类型的整数。


2
使用std::bitset答案和便捷模板:
#include <iostream>
#include <bitset>
#include <climits>

template<typename T>
struct BinaryForm {
    BinaryForm(const T& v) : _bs(v) {}
    const std::bitset<sizeof(T)*CHAR_BIT> _bs;
};

template<typename T>
inline std::ostream& operator<<(std::ostream& os, const BinaryForm<T>& bf) {
    return os << bf._bs;
}


将其翻译成中文如下:

这样使用:

auto c = 'A';
std::cout << "c: " << c << " binary: " << BinaryForm{c} << std::endl;
unsigned x = 1234;
std::cout << "x: " << x << " binary: " << BinaryForm{x} << std::endl;
int64_t z { -1024 };
std::cout << "z: " << z << " binary: " << BinaryForm{z} << std::endl;

生成输出:
c: A binary: 01000001
x: 1234 binary: 00000000000000000000010011010010
z: -1024 binary: 1111111111111111111111111111111111111111111111111111110000000000

0
#include <iostream> 
#include <cmath>       // in order to use pow() function
using namespace std; 

string show_binary(unsigned int u, int num_of_bits);

int main() 
{ 

  cout << show_binary(128, 8) << endl;   // should print 10000000
  cout << show_binary(128, 5) << endl;   // should print 00000
  cout << show_binary(128, 10) << endl;  // should print 0010000000

  return 0; 
}

string show_binary(unsigned int u, int num_of_bits) 
{ 
  string a = "";

  int t = pow(2, num_of_bits);   // t is the max number that can be represented

  for(t; t>0; t = t/2)           // t iterates through powers of 2
      if(u >= t){                // check if u can be represented by current value of t
          u -= t;
          a += "1";               // if so, add a 1
      }
      else {
          a += "0";               // if not, add a 0
      }

  return a ;                     // returns string
}

难道不应该是 int t = pow(2, num_of_bits - 1); 吗? - BmyGuest

0

如果使用旧版的C++,您可以使用以下代码片段:

template<typename T>
string toBinary(const T& t)
{
  string s = "";
  int n = sizeof(T)*8;
  for(int i=n-1; i>=0; i--)
  {
    s += (t & (1 << i))?"1":"0";
  }
  return s;
}

int main()
{
  char a, b;

  short c;
  a = -58;
  c = -315;

  b = a >> 3;

  cout << "a = " << a << " => " << toBinary(a) << endl;
  cout << "b = " << b << " => " << toBinary(b) << endl;
  cout << "c = " << c << " => " << toBinary(c) << endl;
}

a = => 11000110
b = => 11111000
c = -315 => 1111111011000101

打印错误的位数。111 000 110 是9位,而不是8位。 - David Ledger
我犯了边界错误,请现在检查。 - Ratah

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