2010-02-18 4 views
1

У меня возникли проблемы с правильной работой следующего кода. Используя онлайн-конвертер IEEE-754, я написал (вручную) файл testData.txt, который считывается с битовой строкой, которая должна означать номер с плавающей точкой 75.5; фактический cout.write действительно показывает, что бит-строка, как я ожидаю, также. Однако, когда я пытаюсь принудить char * к float с помощью объединения (как я видел, это типичный способ выполнить это преобразование), полученный float не является числом, которое я ожидаю.Bitstream to Float Type Coercion

#include<climits> 
#include<iostream> 
#include<fstream> 
#include<bitset> 

int main(int, char**) 
{ 

    std::ifstream inputFile("testData.txt", std::ios_base::in | std::ios_base::binary); 
    if(!inputFile) std::cout << "Failed to open input file!" << std::endl; 

    char buffer[ CHAR_BIT * sizeof(float) ]; 
    inputFile.read(buffer, CHAR_BIT * sizeof(float)); 

    std::cout << "cout.write of input from file = "; 
    std::cout.write(buffer, CHAR_BIT * sizeof(float)); 
    std::cout << std::endl; 

    union { float f; char* c; } fToCharStarUnion; 

    fToCharStarUnion.c = buffer; 
    std::bitset< sizeof(float) * CHAR_BIT > bits(std::string(fToCharStarUnion.c)); 
    std::cout << "fToCharStarUnion.f = " << fToCharStarUnion.f << " bits = " << bits << std::endl; 

    inputFile.close(); 
    return 0; 
} 

Ответный результат работы это:

cout.write of input from file = 01000010100101110000000000000000 
fToCharStarUnion.f = -1.61821e+38 bits = 01000010100101110000000000000000 

Есть ли что-то фундаментальное я не делаю что сделает эту работу правильно?

ответ

3

Вы переводите ASCII в биты, используя конструктор bitset. Это заставляет ваши декодированные биты находиться в объекте bitset, а не union. Для того, чтобы получить исходные биты из BitSet, используйте to_ulong метод:

#include<climits> 
#include<iostream> 
#include<fstream> 
#include<bitset> 

int main(int, char**) 
{ 

    std::ifstream inputFile("testData.txt", 
     std::ios_base::in | std::ios_base::binary); 
    if(!inputFile) std::cout << "Failed to open input file!" << std::endl; 

    char buffer[ CHAR_BIT * sizeof(float) ]; 
    inputFile.read(buffer, CHAR_BIT * sizeof(float)); 

    std::cout << "cout.write of input from file = "; 
    std::cout.write(buffer, CHAR_BIT * sizeof(float)); 
    std::cout << std::endl; 

    union { 
     float f[ sizeof(unsigned long)/sizeof(float) ]; 
     unsigned long l; 
    } funion; 

    funion.l = std::bitset<32>(std::string(buffer)).to_ulong(); 
    std::cout << "funion.f = " << funion.f[0] 
     << " bits = " << std::hex <<funion.l << std::endl; 

    inputFile.close(); 
    return 0; 
} 

Как правило, это предполагает, что ваш FPU работает с такой же порядок байтов, как целая часть вашего процессора, и что sizeof(long) >= sizeof(float) ... менее гарантироваться double и на самом деле трюк сложнее сделать переносным для 32-битных машин с 64-битными FPU.

Редактировать: Теперь, когда я сделал члены союза равными по размеру, я вижу, что этот код чувствителен к контенту. Декодированный float будет в последнем элементе массива на машине большого конца, первый элемент на малопонятном. : v. Возможно, лучшим подходом было бы попытаться дать целочисленному члену союза точно столько же бит, сколько член FP, и выполнить сужающийся приём после получения to_ulong. Очень сложно поддерживать стандарт переносимости, который вам показался снимая в исходном коде.

+0

Я читал, что вы отвечаете, он полностью исправляет мою проблему, поэтому большое вам спасибо. Я отмечу это как ответ, но я хотел посмотреть, представлял ли Джонатан Леффлер что-то другое, прежде чем я это сделал. Я также предпочел бы использовать решение, в котором был бы массив символов с типом unsigned long int как другой член союза (потому что неясно, как расширить объединение к любому другому типу, чем поплавать мне хотя бы, но в char [] случай тривиален). – bpw1621

+0

@bpw: Если вы используете 'char []' для хранения битового массива, каждый объект 'char' хранит 8 бит. Если вы используете 'long []' для хранения битового массива, каждый 'long'-объект хранит sizeof (long) * 8 бит. К сожалению, 'bitset' будет возвращать вам только бит' long', поэтому вы существенно ограничены 'long [1]'. Это действительно предел 'bitet', и вам нужно будет закодировать свой собственный, чтобы обойти его. – Potatoswatter

+0

@bpw (from other thread): Вы можете замять один пакет в «union», по крайней мере. У всех членов профсоюза гарантируется одинаковый адрес. Серия пакетов может быть последовательностью указателей объединения в буфер. Используйте этот начальный 'int', чтобы определить, какой именно объект каждый союз действительно ... это поведение определено и гарантировано безопасным с помощью C++ §9.5/1. Однако не забудьте выполнить преобразование endian для каждого участника. – Potatoswatter

4

Ваш союз должен включать массив символов, а не указатель.

union { float f; char c[sizeof(float)]; } float2char; 

Вам также придется беспокоиться о контенте; есть c [0] конец экспоненты поплавка или хвост мантиссы. (Ответ будет зависеть от вашего оборудования - Intel vs PPC или SPARC или ...)

+0

Изменил союз, как указано выше, использовал вызов memcpy, чтобы поместить буфер в переменную float union, но ответ по-прежнему не прав. Я использую Intel. Я вижу, что вы не умножали sizeof (float) by CHAR_BIT ... возможно ли, что моя проблема связана с тем, что мой файл был выписан вручную в ascii? Я думал, что это было учтено ifstream ios_base :: двоичный флаг. – bpw1621

+0

Я только нужно столько символов, сколько есть байтов в float - следовательно, sizeof (float) корректен. На большинстве машин вам будет присвоено 32-байтовое значение, если вы умножаетесь на CHAR_BIT, но большинство типов float a re всего 4 байта. –

+0

Я понимаю, что вы говорите (и это имеет смысл), но если это так, я думаю, что что-то не так с тем, как я «читаю» данные. Если я читаю только символы sizeof (float), я получаю только одно слово, то есть 0100 вместо 01000010100101110000000000000000 – bpw1621