如何在 C++ 中输出无穷大和 NaN 的位模式?(IEEE 标准)

How can I output bit pattern of infinity and NaN in C++?(IEEE standard)

我正在阅读计算机系统:程序员的视角,然后我找到了Special Values的定义和相应的位模式。

现在,我想使用 C++ 输出它们的位。我用他们的宏来输出位,显然是不正确的,因为宏定义为 Integer!

#define FP_NAN      0x0100
#define FP_NORMAL   0x0400
#define FP_INFINITE (FP_NAN | FP_NORMAL)

如何正确输出上图中的位?以及,为什么编译器定义了那些整数宏而不是 IEEE 标准?

下面是我的代码。

#include <iostream>
#include <cmath>
#include <bitset>

using namespace std;

union U {
    float f;
    int i;
};

int main() {

    U u1, u2;

    u1.f = FP_NAN;
    u2.f = FP_INFINITE;

    cout << bitset<32>(u1.i) << endl;
    cout << bitset<32>(u2.i) << endl;

    return 0;
}

输出:

01000011100000000000000000000000
01000100101000000000000000000000

我的电脑环境:

前阵子写了一个quick-and-dirtydoublebit-wise输出程序。您可以修改它以适用于 float.

其中包含 ANSI 转义序列,可能不适合您的环境。

关键部分只是使用字节内存指针并直接检查位状态,而不是试图让 std::bitset 玩得开心。

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>

using std::cout;
using std::fpclassify;
using std::memcpy;
using std::nan;
using std::numeric_limits;
using std::reverse;
using std::setw;
using std::size_t;
using std::string;
using std::stringstream;
using std::uint32_t;
using std::uint64_t;

namespace {

uint32_t low32_from(double d) {
    char const* p = reinterpret_cast<char const*>(&d);
    uint32_t result;
    memcpy(&result, p, sizeof result);
    return result;
}

uint32_t high32_from(double d) {
    char const* p = reinterpret_cast<char const*>(&d);
    p += 4;
    uint32_t result;
    memcpy(&result, p, sizeof result);
    return result;
}

string hexstr(uint32_t value) {
    char hex[] = "0123456789ABCDEF";
    unsigned char buffer[4];
    memcpy(buffer, &value, sizeof buffer);
    auto p = &buffer[0];
    stringstream ss;
    char const* sep = "";
    for (size_t i = 0; i < sizeof buffer; ++i) {
        ss << sep << hex[(*p >> 4) & 0xF] << hex[*p & 0xF];
        sep = " ";
        ++p;
    }

    return ss.str();
}

string bits(uint64_t v, size_t len) {
    string s;
    int group = 0;
    while (len--) {
        if (group == 4) { s.push_back('\''); group = 0; }
        s.push_back(v & 1 ? '1' : '0');
        v >>= 1;
        ++group;
    }
    reverse(s.begin(), s.end());
    return s;
}

string doublebits(double d) {
    auto dx = fpclassify(d);
    unsigned char buffer[8];
    memcpy(buffer, &d, sizeof buffer);
    stringstream ss;
    uint64_t s = (buffer[7] >> 7) & 0x1;
    uint64_t e = ((buffer[7] & 0x7FU) << 4) | ((buffer[6] >> 4) & 0xFU);
    uint64_t f = buffer[6] & 0xFU;
    f = (f << 8) + (buffer[5] & 0xFFU);
    f = (f << 8) + (buffer[4] & 0xFFU);
    f = (f << 8) + (buffer[3] & 0xFFU);
    f = (f << 8) + (buffer[2] & 0xFFU);
    f = (f << 8) + (buffer[1] & 0xFFU);
    f = (f << 8) + (buffer[0] & 0xFFU);

    ss << "sign:3[0;32m" << bits(s, 1) << "3[0m ";
    if (s) ss << "(-) ";
    else ss << "(+) ";

    ss << "exp:3[0;33m" << bits(e, 11) << "3[0m ";
    ss << "(" << setw(5) << (static_cast<int>(e) - 1023) << ") ";


    ss << "frac:";

    // 'i' for implied 1 bit, '.' for not applicable (so things align correctly).
    if (dx == FP_NORMAL) ss << "3[0;34mi";
    else ss << "3[0;37m.3[34m";

    ss << bits(f, 52) << "3[0m";

    if (dx == FP_INFINITE) ss << " 3[35mInfinite3[0m";
    else if (dx == FP_NAN) ss << " 3[35mNot-A-Number3[0m";
    else if (dx == FP_NORMAL) ss << " 3[35mNormal3[0m";
    else if (dx == FP_SUBNORMAL) ss << " 3[35mDenormalized3[0m";
    else if (dx == FP_ZERO) ss << " 3[35mZero3[0m";

    ss << " " << d;

    return ss.str();
}

} // anon

int main() {
    auto lo = low32_from(1111.2222);
    auto hi = high32_from(1111.2222);
    cout << hexstr(lo) << "\n";
    cout << hexstr(hi) << "\n";
    cout << doublebits(1111.2222) << "\n";
    cout << doublebits(1.0) << "\n";
    cout << doublebits(-1.0) << "\n";
    cout << doublebits(+0.0) << "\n";
    cout << doublebits(-0.0) << "\n";
    cout << doublebits(numeric_limits<double>::infinity()) << "\n";
    cout << doublebits(-numeric_limits<double>::infinity()) << "\n";
    cout << doublebits(nan("")) << "\n";

    double x = 1.0;
    while (x > 0.0) {
        cout << doublebits(x) << "\n";
        x = x / 2.0;
    }
}

您的代码存在多个问题。

问题 #1:

FP_NAN 和 FP_INFINITE 不是表示 std::fpclassify 的 return 值的常量,return 是给定浮点数的分类。

问题 2:

正在访问不活跃的联合成员,即不是最新分配给的,是 UB。最可靠的 well-known 检查对象内存表示的方法是 memcpy 将其放入字符缓冲区。

考虑到这一点,您可以按照以下方式编写代码:

#include <bitset>
#include <cmath> // nanf
#include <cstring> // memcpy
#include <iostream>
#include <limits>
#include <ranges>



template <typename T> // Template, because reusability
void print_bits(const T& t)
{
    char buffer[sizeof(T)];
    std::memcpy(buffer, &t, sizeof(T));
    for (char c: buffer | std::views::reverse) //Endianness
    {
        std::cout << std::bitset<8>(c);
    }
}


int main()
{
    const double nan = std::nanf("");
    const double inf = std::numeric_limits<float>::infinity();
    print_bits(nan);
    std::cout << '\n';
    print_bits(inf);
    std::cout << '\n';

}

0111111111111000000000000000000000000000000000000000000000000000 0111111111110000000000000000000000000000000000000000000000000000

http://coliru.stacked-crooked.com/a/0d6c30067c9e7e6a