在C ++中显示浮点类型的二进制表示

时间:2017-12-31 09:56:48

标签: c++ std binary-data bitset

考虑以下用于整数类型的代码:

template <class T>
std::string as_binary_string( T value ) {
    return std::bitset<sizeof( T ) * 8>( value ).to_string();
}

int main() {
    unsigned char a(2);
    char          b(4);

    unsigned short c(2);
    short          d(4);

    unsigned int   e(2);
    int            f(4);

    unsigned long long g(2);
    long long h(4);

    std::cout << "a = " << +a << " " << as_binary_string( a ) << std::endl;
    std::cout << "b = " << +b << " " << as_binary_string( b ) << std::endl;
    std::cout << "c = " << c << " " << as_binary_string( c ) << std::endl;
    std::cout << "d = " << c << " " << as_binary_string( d ) << std::endl;
    std::cout << "e = " << e << " " << as_binary_string( e ) << std::endl;
    std::cout << "f = " << f << " " << as_binary_string( f ) << std::endl;
    std::cout << "g = " << g << " " << as_binary_string( g ) << std::endl;
    std::cout << "h = " << h << " " << as_binary_string( h ) << std::endl;

    std::cout << "\nPress any key and enter to quit.\n";
    char q;
    std::cin >> q;

    return 0;
}

非常直接,效果很好而且非常简单。

修改

如何在编译时编写函数来提取任意浮点类型的二进制或位模式?

说到花车我在自己所知的任何现有库中都没有找到类似的东西。我搜索谷歌几天寻找一个,所以我试图编写自己的功能,但没有任何成功。因为我最初问过这个问题所以我不再有可用的代码了,因此我无法准确地向您展示所有不同的实现尝试以及它们的编译器 - 构建错误。我有兴趣尝试在编译期间以通用方式生成浮点数的位模式,并希望将其集成到我现有的类中,无缝地对任何整数类型执行相同的操作。至于浮动类型本身,我已经考虑了不同的格式以及架构端序。对于我的一般用途,浮点类型的标准IEEE版本是我应该关注的所有内容。

当我最初提出这个问题的时候,iBug建议让我写自己的功能,而我正试图这样做。我理解二进制数,内存大小和数学,但是当试图将浮点数类型如何存储在内存中时,它们的不同部分{sign bit,base&amp; exp}是我最麻烦的地方。

从那时起,那些给出了很好答案的建议 - 例如,我能够编写一个能很好地融入我现有的类模板的函数,现在它可以用于我的预期目的。

5 个答案:

答案 0 :(得分:7)

如何自己写一个?

template <typename T>
std::string as_binary_string( T value )
{
    const std::size_t nbytes = sizeof(T), nbits = nbytes * CHAR_BIT;
    std::bitset<nbits> b;
    std::uint8_t buf[nbytes];
    std::memcpy(buf, &value, nbytes);

    for(int i = 0; i < nbytes; ++i)
    {
        std::uint8_t cur = buf[i];
        int offset = i * CHAR_BIT;

        for(int bit = 0; bit < CHAR_BIT; ++bit)
        {
            b[offset] = cur & 1;
            ++offset;   // Move to next bit in b
            cur >>= 1;  // Move to next bit in array
        }
    }

    return b.to_string();
}

如果浮点数的大小不同,您可能需要更改辅助变量{{1}}。

您也可以逐位复制它们。这个速度较慢,但​​任意类型都可以使用。

{{1}}

答案 1 :(得分:2)

你说它不需要是标准的。所以,这是在我的计算机上使用clang的方法:

#include <iostream>
#include <algorithm>

using namespace std;

int main()
{
  char *result;
  result=new char[33];
  fill(result,result+32,'0');
  float input;
  cin >>input;
  asm(
"mov %0,%%eax\n"
"mov %1,%%rbx\n"
".intel_syntax\n"
"mov rcx,20h\n"
"loop_begin:\n"
"shr eax\n"
"jnc loop_end\n"
"inc byte ptr [rbx+rcx-1]\n"
"loop_end:\n"
"loop loop_begin\n"
".att_syntax\n"
      :
      : "m" (input), "m" (result)
      );
  cout <<result <<endl;
  delete[] result;
  return 0;
}

这段代码对计算机体系结构做了一系列假设,我不知道它会使用多少台计算机。

修改
我的电脑是64位Mac-Air。该程序基本上通过分配一个33字节的字符串并用'0'填充前32个字节(第33个字节将自动为'\0')。 然后它使用内联汇编将 float 存储到32位寄存器中,然后重复将其向右移位一位。
如果在移位之前寄存器中的最后一位是1,则它将被存储到进位标志中 然后汇编代码检查进位标志,如果它包含1,则它将字符串中的相应字节增加1 由于之前已初始化为'0',因此会转为'1'

因此,实际上,当汇编中的循环完成时, float 的二进制表示将存储到字符串中。

此代码仅适用于x64(它使用64位寄存器"rbx""rcx"来存储指针和循环计数器),但我认为很容易调整它来处理其他处理器。

答案 2 :(得分:2)

IEEE浮点数如下所示

sign  exponent   mantissa
1 bit  11 bits    52 bits 

请注意,尾数前面有一个隐藏的1和指数 有偏见所以1023 = 0,而不是两个补码。 通过memcpy()转换为64位无符号整数,然后可以应用AND和 OR掩码以获得位模式。这种安排可能是大端 或小端。 通过传递简单的数字,您可以轻松地确定您的安排 例如1或2.

答案 3 :(得分:2)

通常人们使用std::hexfloat或将指向浮点值的指针强制转换为指向相同大小的无符号整数的指针,并以十六进制格式打印出间接值。这两种方法都能以高效的方式促进浮点的位级分析。

答案 4 :(得分:0)

你可以通过将float / double的地址转换为char并以这种方式迭代来滚动你:

#include <memory>
#include <iostream>
#include <limits>
#include <iomanip>

template <typename T>
std::string getBits(T t) {
    std::string returnString{""};
    char *base{reinterpret_cast<char *>(std::addressof(t))};
    char *tail{base + sizeof(t) - 1};
    do {
        for (int bits = std::numeric_limits<unsigned char>::digits - 1; bits >= 0; bits--) {
            returnString += ( ((*tail) & (1 << bits)) ? '1' : '0');
        }
    } while (--tail >= base);
    return returnString;
}

int main() {
    float f{10.0};
    double d{100.0};
    double nd{-100.0};
    std::cout << std::setprecision(1);
    std::cout << getBits(f) << std::endl;
    std::cout << getBits(d) << std::endl;
    std::cout << getBits(nd) << std::endl;
}

我的机器上的输出(注意第三个输出中的符号翻转):

01000001001000000000000000000000
0100000001011001000000000000000000000000000000000000000000000000
1100000001011001000000000000000000000000000000000000000000000000