【问题标题】:How to print (using cout) a number in binary form?如何打印(使用 cout)二进制形式的数字?
【发布时间】:2011-11-13 01:30:22
【问题描述】:

我正在学习关于操作系统的大学课程,我们正在学习如何从二进制转换为十六进制、从十进制转换为十六进制等。今天我们刚刚学习了如何使用二进制补码将有符号/无符号数字存储在内存中(~数字 + 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++ 中是否有标准方法来显示数字内存中的二进制表示,还是我必须自己编写每个步骤(计算二进制补码然后转换为二进制)?我知道后者不会花这么长时间,但我很好奇是否有标准的方法来做到这一点。

【问题讨论】:

  • 你了解十六进制表示吗?如果你这样做了,你可以打印十六进制表示(使用std::hex)操纵器 - 我会把它作为练习留给你来解决剩下的......
  • 您非常强调“内存中”,但我希望它们不会让您处理字节序问题。
  • 你知道什么是字节序吗?如果你这样做了,你关心这个练习吗?这些问题的答案可能会影响您的问题的答案。
  • 根据您的 IDE,如果您只是想验证您的手写解决方案的正确性,而不是实际编写程序来显示有用的内容,您可以使用 Visual Studio 的内存查看器之类的工具来查看内存的确切内容。
  • 即使是 Google 也会这样做,例如 “-58 in binary” - 但 +1 表示想了解如何在代码中自己完成。

标签: c++ binary iostream representation std-bitset


【解决方案1】:

最简单的方法可能是创建一个代表值的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';

【讨论】:

  • 请原谅我的无知,但这只会显示数字的二进制表示(例如 8 将是 00001000)或其内存表示(例如,如何通过处理符号位来存储 -8 和使用“二进制补码”)?
  • @Jesse:bitset 的构造函数参数被解释为一个无符号值,其结果与二进制补码相同。严格来说,C++ 不保证二进制补码算法,而且您示例中的-58 &gt;&gt; 3 操作也是未定义的。
  • 我可以将 bitset 值(即本例中的 x 或 y)类型转换为 char* 吗?
  • @nirvanaswap:我想你可以施放它,但结果不太可能有用。如果您需要字符串形式的结果,请使用bitsetto_string 成员。
  • 感谢 Jerry,几分钟后我发现了 to_string。仅供参考,强制转换不起作用, bitset 变量是一些看起来很神秘的 bitset3ul (?!) 类的对象。最好让抽象来完成工作!
【解决方案2】:

使用即时转换为std::bitset。没有临时变量,没有循环,没有函数,没有宏。

Live On 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

【讨论】:

  • 请注意,硬编码大小不是必需的。例如。打印x 使用:std::cout &lt;&lt; std::bitset&lt;8*sizeof(x)&gt;(x)
【解决方案3】:

在 C++20 中,您将能够使用 std::format 来执行此操作:

unsigned char a = -58;
std::cout << std::format("{:b}", a);

输出:

11000110

同时你可以使用the {fmt} librarystd::format是基于。 {fmt} 还提供了print 函数,使这更容易和更高效(godbolt):

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

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

【讨论】:

  • std::format 在 Visual Studio 2019 中尚不可用,是吗?
  • @rturrado std::format 在 Visual Studio 2019 中可用。您必须 #include &lt;format&gt; 并设置 /std:c++latest
  • @ChrisG。感谢您的信息!
【解决方案4】:

如果您想显示任何对象的位表示,而不仅仅是整数,请记住首先将其重新解释为 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,因为这不是它在内存中的存储方式。如果您正在寻找二进制的 value 表示,那么简单的cout &lt;&lt; bitset&lt;16&gt;(c) 就可以了。

【讨论】:

    【解决方案5】:

    在 C++ 中是否有一种标准方法可以在内存中显示数字的二进制表示 [...]?

    没有。没有std::bin,像std::hexstd::dec,但自己输出一个二进制数并不难:

    您通过屏蔽所有其他位、左移来输出最左边的位,然后对您拥有的所有位重复此操作。

    (一个类型的位数是sizeof(T) * CHAR_BIT。)

    【讨论】:

      【解决方案6】:

      和已经发布的类似,只是使用位移位和掩码来获取位;可用于任何类型,作为模板(只是不确定是否有标准方法来获取 1 个字节中的位数,我在这里使用了 8 个)。

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

      【讨论】:

      • 获取每字节位数的标准方法是宏CHAR_BIT
      • 根据@R.MartinhoFernandes 的评论,sbi 似乎编辑了Εύδοξος 的帖子。但是,他并没有改变最后一句话。我会编辑。
      【解决方案7】:

      可复用功能:

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

      这适用于所有类型的整数。

      【讨论】:

        【解决方案8】:
        #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); 吗?
        【解决方案9】:

        使用旧的 C++ 版本,你可以使用这个 sn -p :

        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 位。
        • 我犯了边界错误,请立即检查
        【解决方案10】:

        使用 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
        

        【讨论】:

          【解决方案11】:

          这是获得数字二进制表示的真正方法:

          unsigned int i = *(unsigned int*) &x;
          

          【讨论】:

          • 否;这只是将 x 复制到 i 中。除非你是开玩笑的?
          【解决方案12】:

          这是你要找的吗?

          std::cout << std::hex << val << std::endl;
          

          【讨论】:

          • 版主说明试图在这个答案下有选择地清除对抗性或非建设性的 cmet,结果我的谈话非常破裂。清除所有 cmets。请保持 cmets 的专业性、建设性和最重要的主题。如果 OP 想要删除它,那么 OP 早就将其删除了。如果您不同意这个答案,请投票。如果您可以改进此答案,请编辑。 &lt;/argument&gt;。真的,我们是成年人,是吗?我几乎检查了所有评论的年龄,以确保每个人都超过 13 岁。
          猜你喜欢
          • 2018-06-15
          • 2010-11-04
          • 1970-01-01
          • 1970-01-01
          • 2016-12-13
          • 2011-08-04
          • 1970-01-01
          相关资源
          最近更新 更多