Битовое представление типа float с использованием указателя int - PullRequest
0 голосов
/ 30 сентября 2018

У меня есть следующее упражнение:

Реализация функции void с плавающей запятой в битах (с плавающей запятой x), которая печатает представление битов x.Подсказка: приведение числа с плавающей точкой к int усекает дробную часть, но информация не теряется при наведении указателя с плавающей точкой на указатель на int.

Теперь я знаю, что число с плавающей точкой представлено битом знаканекоторые биты для его мантиссы, некоторые биты для основы и несколько бит для показателя степени.Сколько бит используется, зависит от моей системы.

Проблема, с которой мы здесь сталкиваемся, состоит в том, что наше число состоит из двух частей.Давайте рассмотрим 8.7, битовое представление этого числа будет (на мой взгляд) следующим: 1000.0111

Теперь числа с плавающей запятой хранятся с начальным нулем, поэтому 8.8 станет 0.88*10^1

Так что я каким-то образом должен вытащить всю информацию из моей памяти.Я действительно не понимаю, как я должен это сделать.На что мне намекает этот намек?В чем разница между целочисленным указателем и указателем с плавающей точкой?

В настоящее время у меня есть это:

void float_to_bits() {
    float a = 4.2345678f;
    int* b;
    b = (int*)(&a);
    *b = a;

    std::cout << *(b) << "\n";
}

Но я действительно не вижу здесь большей картины за подсказкой.Как мне получить мантиссу, показатель степени, знак и основу?Я также попытался поиграть с побитовыми операторами >>, <<.Но я просто не понимаю, как это должно мне здесь помочь, поскольку они не изменят позицию указателей.Полезно получить, например, битовое представление целого числа, но это все, понятия не имею, для чего он здесь нужен. </p>

Ответы [ 2 ]

0 голосов
/ 30 сентября 2018

Подсказка указывает, как передать число с плавающей точкой в ​​целое число без преобразования значения.
Когда вы присваиваете целое число с плавающей запятой, процессор удаляет часть дроби.int i = (int) 4.502f; приведет к i = 4;

, но когда вы делаете указатель int (int*), указывающий на местоположение с плавающей точкой, преобразование не выполняется, даже когда вы читаете значение int*.

, чтобы показать представление, мне нравится видеть шестнадцатеричные числа,
, поэтому мой первый пример был дан в шестнадцатеричном
(каждая шестнадцатеричная цифра представляет 4 двоичные цифры).

, нотакже возможно распечатать в двоичном виде,
, и есть много способов (мне нравится этот самый лучший!)

Следует аннотированному примеру кода:
Также доступно @ Culio

#include <iostream>
#include <bitset>
using namespace std;

int main()
{
   float a = 4.2345678f; // allocate space for a float. Call it 'a' and put the floating point value of `4.2345678f` in it.
    unsigned int* b; // allocate a space for a pointer (address), call the space b, (hint to compiler, this will point to integer number)
    b = (unsigned int*)(&a); // GREAT, exactly what you needed! take the float 'a', get it's address '&'.
    //                          by default, it is an address pointing at float (float*) , so you correctly cast it to (int*).
    //                          Bottom line: Set 'b' to the address of a, but treat this address of an int!

    // The Hint implied that this wont cause type conversion:
    // int someInt = a; // would cause `someInt = 4` same is your line below:
    // *b = a; // <<<< this was your error.
    // 1st thing, it aint required, as 'b' already pointing to `a` address, hence has it's value.
    // 2nd by this, you set the value pointed by `b` to 'a' (including conversion to int = 4);
    // the value in 'a' actually changes too by this instruction.

    cout << a << " in binary " << bitset<32>(*b)  << endl;
    cout << "Sign    " << bitset<1>(*b >> 31) << endl; // 1 bit (31)
    cout << "Exp     " << bitset<8>(*b >> 23) << endl; // 8 bits (23-30)
    cout << "Mantisa " << bitset<23>(*b) << endl; // 23 bits (0-22)
}
0 голосов
/ 30 сентября 2018

Намек, который дал ваш учитель, вводит в заблуждение: указатель приведения между различными типами в лучшем случае определяется реализацией.Однако memcpy(...) выводит объект в массив достаточного размера, если определено unsigned char.Содержимое полученного массива затем может быть разложено на биты.Вот быстрый способ представить биты шестнадцатеричными значениями:

#include <iostream>
#include <iomanip>
#include <cstring>

int main() {
    float f = 8.7;
    unsigned char bytes[sizeof(float)];
    std::memcpy(bytes, &f, sizeof(float));
    std::cout << std::hex << std::setfill(‘0’);
    for (int b: bytes) {
        std::cout << std::setw(2) << b;
    }
    std::cout << ‘\n’;
}

Обратите внимание, что двоичные числа с плавающей запятой IEEE 754 не хранят полное значение и (стандарт не использует mantissa как термин) за исключением денормализованных значений: 32-разрядное хранилище с плавающей запятой

  • 1 бит для знака
  • 8 бит для показателя степени
  • 23 бита для нормализованного значения с подразумеваемым ненулевым старшим битом
...