Преобразование 4 необработанных байтов в 32-битную с плавающей точкой
Я пытаюсь воссоздать 32-разрядное значение с плавающей точкой из EEPROM.
4 байта в памяти EEPROM (0-4): B4 A2 91 4D
и ПК (VS Studio) восстанавливает его правильно как 3.054199 * 10^8 (значение с плавающей запятой, которое я знаю, должно быть там)
Теперь я перемещаю этот eeprom для чтения из 8-битного Arduino, поэтому не уверен, что это вещь компилятора / платформы, но когда я пытаюсь прочитать 4 байта в 32-битном dword, а затем типизировать его на float, ценность, которую я получаю, даже не близка.
Предполагая, что преобразование не может быть выполнено автоматически с помощью стандартного компилятора ANSI-C, как можно вручную проанализировать 4 байта, чтобы получить число с плавающей запятой?
3 ответа
Самый безопасный способ и благодаря оптимизации компилятора также быстро, как и любой другой, это использовать memcpy
:
uint32_t dword = 0x4D91A2B4;
float f;
memcpy(&f, &dw, 4);
Демо: http://ideone.com/riDfFw
Как отметил в своем ответе Shafik Yaghmour - это, вероятно, проблема с порядком байтов, поскольку это единственная логическая проблема, с которой вы можете столкнуться при такой операции низкого уровня. В то время как Шафикс отвечает на вопрос, который он связал, в основном охватывает процесс решения такой проблемы, я просто оставлю вам некоторую информацию:
Как заявлено на форумах Anduino, Anduino использует Little Endian. Если вы не уверены в том, что будет с порядком байтов системы, над которой вы в конечном итоге будете работать, но хотите сделать свой код полу-мультиплатформенным, вы можете проверить порядок байтов во время выполнения с помощью простого фрагмента кода:
bool isBigEndian(){
int number = 1;
return (*(char*)&number != 1);
}
Имейте в виду, что - как и все - это отнимает некоторое время вашего процессора и замедляет работу вашей программы, и хотя это почти всегда плохо, вы все равно можете использовать это, чтобы увидеть результаты в отладочной версии вашего приложения.
Как это работает, так это то, что он проверяет первый байт int
хранится по адресу, указанному &number
, Если первый байт не 1
, это означает, что байты являются Big Endian.
Кроме того - это будет работать только если sizeof(int) > sizeof(char)
,
Вы также можете встроить это в свой код:
float getFromEeprom(int address){
char bytes[sizeof(float)];
if(isBigEndian()){
for(int i=0;i<sizeof(float);i++)
bytes[sizeof(float)-i] = EEPROM.read(address+i);
}
else{
for(int i=0;i<sizeof(float);i++)
bytes[i] = EEPROM.read(address+i);
}
float result;
memcpy(&result, bytes, sizeof(float));
return result;
}
Вам нужно привести на уровне указателя.
int myFourBytes = /* something */;
float* myFloat = (float*) &myFourBytes;
cout << *myFloat;
Должно сработать.
Если данные генерируются на другой платформе, в которой значения хранятся с обратным порядком байтов, вам нужно вручную поменять местами байты. Например:
unsigned char myFourBytes[4] = { 0xB4, 0xA2, 0x91, 0x4D };
std::swap(myFourBytes[0], myFourBytes[3]);
std::swap(myFourBytes[1], myFourBytes[2]);