С плавающей запятой к двоичному значению (C ++) - PullRequest
14 голосов
/ 23 января 2009

Я хочу взять число с плавающей запятой в C ++, например 2.25125, и массив int, заполненный двоичным значением, которое используется для хранения числа с плавающей запятой в памяти (IEEE 754).

Итак, я мог бы взять число и получить массив типа int num [16] с двоичным значением типа float: num [0] будет 1 num [1] будет 1 num [2] будет 0 num [3] будет 1 и так далее ...

Поместить int в массив не сложно, просто я застрял в процессе получения двоичного значения с плавающей точкой. Можете ли вы просто прочитать двоичный файл в памяти, что переменная с плавающей точкой? Если нет, то как я могу сделать это в C ++?

РЕДАКТИРОВАТЬ: причина для сравнения таким образом, что я хочу научиться выполнять побитовые операции в C ++.

Ответы [ 11 ]

22 голосов
/ 23 января 2009

Использовать объединение и набор битов:

#include <iostream>
#include <bitset>

int main()
{
    union
    {
         float input;   // assumes sizeof(float) == sizeof(int)
         int   output;
    }    data;

    data.input = 2.25125;

    std::bitset<sizeof(float) * CHAR_BIT>   bits(data.output);


    std::cout << bits << std::endl;

    // or

    std::cout << "BIT 4: " << bits[4] << std::endl;
    std::cout << "BIT 7: " << bits[7] << std::endl;
}

Возможно, это не массив, но вы можете получить доступ к битам с помощью оператора [], как если бы вы использовали массив.

выход

$ ./bits
01000000000100000001010001111011
BIT 4: 1
BIT 7: 0
14 голосов
/ 23 января 2009
int fl = *(int*)&floatVar; //assuming sizeof(int) = sizeof(float)

int binaryRepresentation[sizeof(float) * 8];

for (int i = 0; i < sizeof(float) * 8; ++i)
    binaryRepresentation[i] = ((1 << i) & fl) != 0 ? 1 : 0;

Объяснение

(1 << i) сдвигает значение 1, i бит влево. Оператор & вычисляет битовые и операндов.

Цикл for запускается один раз для каждого из 32 битов с плавающей точкой. Каждый раз i будет номером бита, из которого мы хотим извлечь значение. Мы вычисляем поразрядно и числа и 1 << i:

Предположим, что число: 1001011 и i = 2

1<<i будет равно 0000100

  10001011
& 00000100
==========
  00000000

если i = 3, то:

  10001011
& 00001000
==========
  00001000

По сути, результатом будет число с i -ым битом, установленным на i -й бит по сравнению с исходным числом, а все остальные биты равны нулю. Результат будет равен нулю, что означает, что i -й бит в исходном числе был равен нулю, или ненулевой, что означает, что действительное число имеет i -й бит, равный 1.

4 голосов
/ 19 марта 2014

другой подход, используя stl

#include <iostream>
#include <bitset>

using namespace std;
int main()
{
    float f=4.5f;
    cout<<bitset<sizeof f*8>(*(long unsigned int*)(&f))<<endl;
    return 0;
}
2 голосов
/ 23 января 2009

Глядя на комментарии в этом ответе ( С плавающей запятой к двоичному значению (C ++) ), причина сделать это - выполнить побитовое сравнение двух значений.

#include <iostream>

int main()
{
    union Flip
    {
         float input;   // assumes sizeof(float) == sizeof(int)
         int   output;
    };

    Flip    data1;
    Flip    data2;
    Flip    data3;

    data1.input = 2.25125;
    data2.input = 2.25126;
    data3.input = 2.25125;

    bool    test12  = data1.output ^ data2.output;
    bool    test13  = data1.output ^ data3.output;
    bool    test23  = data2.output ^ data3.output;

    std::cout << "T1(" << test12 << ") T2(" << test13 << ") T3(" << test23 << ")\n";


}
2 голосов
/ 23 января 2009

Вы можете использовать беззнаковый символ для чтения байта с плавающей запятой в массив целых чисел:

unsigned int bits[sizeof (float) * CHAR_BIT];
unsigned char const *c = static_cast<unsigned char const*>(
    static_cast<void const*>(&my_float)
);

for(size_t i = 0; i < sizeof(float) * CHAR_BIT; i++) {
    int bitnr = i % CHAR_BIT;
    bits[i] = (*c >> bitnr) & 1;
    if(bitnr == CHAR_BIT-1)
        c++;
}

// the bits are now stored in "bits". one bit in one integer.

Кстати, если вы просто хотите сравнить биты (когда вы комментируете другой ответ), используйте memcmp:

memcmp(&float1, &float2, sizeof (float));
2 голосов
/ 23 января 2009

Можете ли вы просто прочитать двоичный файл в памяти, который является переменной float?

Да. Static приводит указатель на него к указателю на int и считывает биты из результата. Тип IEEE 754 float в C ++ - 32 бита.

2 голосов
/ 23 января 2009

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

c0x стандарт: http://c0x.coding -guidelines.com / 5.2.4.2.2.html не определяет формат чисел с плавающей запятой.

1 голос
/ 23 января 2009

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

Это исключит операторы приведения.

1 голос
/ 23 января 2009

Приведите указатель типа int к указателю с плавающей точкой, и все готово.

(Хотя я бы не объявил его как массив int. Я бы использовал void *, чтобы было ясно, что память используется в качестве полигона для других значений.)

Кстати, почему бы вам просто не использовать массив с плавающей точкой?

0 голосов
/ 28 декабря 2012

Ну, я не верю, что в C ++ есть какой-то реальный безопасный способ хранения чисел без каких-либо проблем. Когда дело доходит до перемещения между машинами, оно эффективно и легко хранится без большой емкости.

Это очень точно, но он не будет поддерживать действительно безумные значения. Вы можете иметь до 7 цифр в любом месте, но вы не можете превышать 7 цифр с обеих сторон. Слева вы получите неточные результаты. Справа вы получите ошибку во время чтения. Чтобы устранить ошибку, вы можете выдать ошибку во время записи или выполнить «buffer [idx ++] & 0x7» для чтения, чтобы предотвратить выход за пределы 0 и 7 границ. Имейте в виду, что «& 0x7» работает только потому, что это степень 2 минус один. Что составляет 2 ^ 3 - 1. Вы можете сделать это только с этими значениями, например, 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 и т.д ...

Так что вам решать, хотите вы это использовать или нет. Я чувствовал, что это был безопасный способ получить большинство ценностей, которые вам когда-либо понадобятся. В приведенном ниже примере показано, как он преобразуется в 4-байтовый массив, но для C ++ это будет символ *. Если вы не хотите выполнять деление, вы можете преобразовать массив POWERS_OF_TEN во вторичный массив с десятичными и кратными вместо него.

const float CacheReader::POWERS_OF_TEN[] = 
{
    1.0F, 10.0F, 100.0F, 1000.0F, 10000.0F, 100000.0F, 1000000.0F, 10000000.0F
};

float CacheReader::readFloat(void)
{
    int flags = readUnsignedByte();
    int value = readUnsignedTriByte();
    if (flags & 0x1)
        value = -value;
    return value / POWERS_OF_TEN[(flags >> 1) & 0x7];
}

unsigned __int32 CacheReader::readUnsignedTriByte(void)
{
    return (readUnsignedByte() << 16) | (readUnsignedByte() << 8) | (readUnsignedByte());
}

unsigned __int8 CacheReader::readUnsignedByte(void)
{
    return buffer[reader_position] & 0xFF;
}

void CacheReader::writeFloat(float data)
{
    int exponent = -1;
    float ceiling = 0.0F;

    for ( ; ++exponent < 8; )
    {
        ceiling = (POWERS_OF_TEN[exponent] * data);
        if (ceiling == (int)ceiling)
            break;
    }

    exponent = exponent << 0x1;
    int ceil = (int)ceiling;
    if (ceil < 0)
    {
        exponent |= 0x1;
        ceil = -ceil;
    }
    buffer[writer_position++] = (signed __int16)(exponent);
    buffer[writer_position++] = (signed __int16)(ceil >> 16);
    buffer[writer_position++] = (signed __int16)(ceil >> 8);
    buffer[writer_position++] = (signed __int16)(ceil);
}
...