以C++显示浮点类型的二进制表示形式

Showing binary representation of floating point types in C++

本文关键字:二进制 表示 类型 C++ 显示      更新时间:2023-10-16

考虑以下整型代码:

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 建议我编写自己的函数,而我正在尝试这样做。我理解二进制数,内存大小和数学,但是当试图将它们与浮点类型如何存储在一起时,它们的不同部分{符号位,基数和exp}是我遇到最多麻烦的地方。

从那时起,有了那些给出很好答案的建议 - 示例,我能够编写一个非常适合我已经存在的类模板的函数,现在它可以满足我的预期目的。

自己写一个怎么样?

static_assert(sizeof(float) == sizeof(uint32_t));
static_assert(sizeof(double) == sizeof(uint64_t));
std::string as_binary_string( float value ) {
std::uint32_t t;
std::memcpy(&t, &value, sizeof(value));
return std::bitset<sizeof(float) * 8>(t).to_string();
}
std::string as_binary_string( double value ) {
std::uint64_t t;
std::memcpy(&t, &value, sizeof(value));
return std::bitset<sizeof(double) * 8>(t).to_string();
}

您可能需要更改帮助程序变量t以防浮点数的大小不同。

您也可以逐位复制它们。这较慢,但适用于任意任何类型的。

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

你说它不需要是标准的。所以,这是我的电脑上在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,%%eaxn"
"mov %1,%%rbxn"
".intel_syntaxn"
"mov rcx,20hn"
"loop_begin:n"
"shr eaxn"
"jnc loop_endn"
"inc byte ptr [rbx+rcx-1]n"
"loop_end:n"
"loop loop_beginn"
".att_syntaxn"
:
: "m" (input), "m" (result)
);
cout <<result <<endl;
delete[] result;
return 0;
}

这段代码对计算机体系结构做出了一堆假设,我不确定它可以在多少台计算机上工作。

编辑:
我的电脑是64位Mac-Air。该程序基本上通过分配一个 33 字节的字符串并用'0'填充前 32 个字节来工作(第 33 个字节将自动'')。
然后,它使用内联程序集将浮点数存储到 32 位寄存器中,然后反复将其向右移动一位。
如果寄存器中的最后一个位在移位前为 1,则它将被存储到进位标志中。
然后,程序集代码检查 carry 标志,如果它包含 1,则它将字符串中的相应字节增加 1。
由于它之前被初始化为'0',它将变成'1'

因此,实际上,当程序集中的循环完成时,浮点数的二进制表示形式将存储到字符串中。

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

IEEE 浮点数如下所示

sign  exponent   mantissa
1 bit  11 bits    52 bits 

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

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

您可以通过将浮点数/双精度数的地址转换为字符并以这种方式迭代它来滚动您的:

#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