Преобразование 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]);
Другие вопросы по тегам