Как я могу вывести битовый шаблон бесконечности и NaN в С++? (Стандарт IEEE)

Я читаю Компьютерные системы: взгляд программиста, потом нашел определение Special Values и соответствующие битовые комбинации.

bit pattern of Infinity and NaN

Теперь я хочу вывести их биты с помощью C++. Я использую их макрос для вывода битов, очевидно, это неправильно, потому что макрос определен как целое число!

#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

Моя компьютерная среда:

  • победа10
  • mingw64

Какой компилятор вы используете и из какого заголовочного файла вы получили эти значения? Что еще говорится в книге/статье об этих битовых шаблонах? Он упомянул тип даты? Архитектура?

lurker 23.04.2022 14:34

Можете ли вы поделиться своим выходом?

user10091872 23.04.2022 14:35

Извините, я уже обновляю вопрос!

OnlyWick 23.04.2022 14:47

Вы действительно проверяли, что означают эти значения макроса, или вы просто предположили это?

Revolver_Ocelot 23.04.2022 14:47

Кроме того: это UB для доступа к таким полям объединения.

Revolver_Ocelot 23.04.2022 14:48

@luker Просто форма хранения чисел с плавающей запятой.

OnlyWick 23.04.2022 14:48

Эти макросы определяют возможные выходные данные std::fpclassify, они не должны (должны) соответствовать фактическим битовым шаблонам чисел с плавающей запятой.

chtz 23.04.2022 14:49

@chtz Спасибо, что напомнили мне, проблема в том, что я не могу исправить вывод «настоящих» битовых шаблонов ...... или могу проверить битовые шаблоны низкого уровня?

OnlyWick 23.04.2022 14:55

Замените u1.f = FP_NAN; на u1.f = std::nanf("");. Замените u2.f = FP_INFINITE; на u2.f = std::numeric_limits<float>::infinity();. Запись в один член объединения и чтение из другого члена объединения — это поведение undefined в C++; вместо этого используйте std::memcpy.

Eljay 23.04.2022 14:55

@Элджей, лол, большое спасибо!

OnlyWick 23.04.2022 15:11
3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
0
10
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Некоторое время назад я написал быструю и грязную double программу побитового вывода. Вы можете изменить его, чтобы он работал на float.

В нем есть escape-последовательности 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:\033[0;32m" << bits(s, 1) << "\033[0m ";
    if (s) ss << "(-) ";
    else ss << "(+) ";

    ss << "exp:\033[0;33m" << bits(e, 11) << "\033[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 << "\033[0;34mi";
    else ss << "\033[0;37m.\033[34m";

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

    if (dx == FP_INFINITE) ss << " \033[35mInfinite\033[0m";
    else if (dx == FP_NAN) ss << " \033[35mNot-A-Number\033[0m";
    else if (dx == FP_NORMAL) ss << " \033[35mNormal\033[0m";
    else if (dx == FP_SUBNORMAL) ss << " \033[35mDenormalized\033[0m";
    else if (dx == FP_ZERO) ss << " \033[35mZero\033[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, который возвращает классификацию заданного числа с плавающей запятой.

Проблема 2:

Доступ к неактивному члену союза, т. е. не к последнему назначенному, — это UB. Самый надежный и известный способ проверить представление объекта в памяти — это 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

Другие вопросы по теме