При попытке сгенерировать битовую комбинацию с плавающей запятой следующим образом:

std::cout << std::bitset<32>(32.5) << std::endl;

Компилятор генерирует это предупреждение:

warning: implicit conversion from 'double' to 'unsigned long long' changes value
  from 32.5 to 32 [-Wliteral-conversion]
 std::cout << std::bitset<32>(32.5) << std::endl;

Вывод на игнорирование предупреждения :):

00000000000000000000000000100000

Почему набор битов не может обнаружить числа с плавающей запятой и правильно вывести последовательность битов, когда приведение к типу char * и прогулочной памяти показывает правильную последовательность? Это работает, но зависит от порядка байтов и в большинстве случаев нечитаемо:

template <typename T>
  void printMemory(const T& data) {
    const char* begin = reinterpret_cast<const char*>(&data);
    const char* end = begin + sizeof(data);
    while(begin != end)
      std::cout << std::bitset<CHAR_BIT>(*begin++) << " ";
    std::cout << std::endl;
}

Выход:

00000000 00000000 00000010 01000010 

Есть ли причина не поддерживать поплавки? Есть ли альтернатива для поплавков?

3
cedoc 20 Окт 2017 в 23:34

3 ответа

Лучший ответ

Со всеми обычными предупреждениями о не стандартизированных форматах с плавающей запятой, порядке байтов и т. Д. И т. Д.

Вот код, который вероятно будет работать, по крайней мере, на оборудовании x86.

#include <bitset>
#include <iostream>
#include <type_traits>
#include <cstring>

constexpr std::uint32_t float_to_bits(float in)
{
    std::uint32_t result = 0;
    static_assert(sizeof(float) == sizeof(result), "float is not 32 bits");
    constexpr auto size = sizeof(float);
    std::uint8_t buffer[size] = {};
    // note - memcpy through a byte buffer to satisfy the
    // strict aliasing rule.
    // note that this has no detrimental effect on performance
    // since memcpy is 'magic'
    std::memcpy(buffer, std::addressof(in), size);
    std::memcpy(std::addressof(result), buffer, size);
    return result;
}

constexpr std::uint64_t float_to_bits(double in)
{
    std::uint64_t result = 0;
    static_assert(sizeof(double) == sizeof(result), "double is not 64 bits");
    constexpr auto size = sizeof(double);
    std::uint8_t buffer[size] = {};
    std::memcpy(buffer, std::addressof(in), size);
    std::memcpy(std::addressof(result), buffer, size);
    return result;
}


int main()
{
    std::cout << std::bitset<32>(float_to_bits(float(32.5))) << std::endl;
    std::cout << std::bitset<64>(float_to_bits(32.5)) << std::endl;
}

Пример вывода:

01000010000000100000000000000000
0100000001000000010000000000000000000000000000000000000000000000
2
Richard Hodges 20 Окт 2017 в 20:59

Что бы вы ожидали увидеть в своем наборе битов, если бы вы предоставили float? Предположительно какое-то представление двоичного кода IEEE-7545 в большом формате -индийский формат? А как насчет платформ, которые не представляют свои float таким образом, который даже отдаленно похож на это? Должна ли реализация отклоняться назад (вероятно, с потерями), чтобы преобразовать число с плавающей запятой в то, что вы хотите?

Причина этого заключается в том, что не существует стандартного определенного формата для чисел с плавающей точкой. Они даже не должны быть 32 битами. Они просто на большинстве платформ.

C ++ и C будут работать на очень маленьких и / или нечетных платформах. Стандарт не может рассчитывать на то, что «обычно бывает». Существовали / есть компиляторы C / C ++ для 8/16-битных систем 6502, извините, извините за собственный формат с плавающей запятой, который (я думаю) был 6-байтовым объектом, который использовал упакованная кодировка BCD.

По этой же причине целые числа signed также не поддерживаются. Два дополнения не универсальны, просто почти универсальны. :-)

3
Omnifarious 21 Окт 2017 в 00:07
#include <iostream>
#include <bitset>
#include <climits>
#include <iomanip>

using namespace std;

template<class T>
auto toBitset(T x) -> bitset<sizeof(T) * CHAR_BIT>
{
    return bitset<sizeof(T) * CHAR_BIT>{ *reinterpret_cast<unsigned long long int *>(&x) };
}

int main()
{
    double x;
    while (cin >> x) {
        cout << setw(14) << x << " " << toBitset(x) << endl;
    }

    return 0;
}

https://wandbox.org/permlink/tCz5WwHqu2X4CV1E

К сожалению, это не удастся, если тип аргумента больше размера unsigned long long, например, это не удастся для long double. Это предел конструктора bitset.

1
Marek R 20 Окт 2017 в 21:42