【问题标题】:Converting 4 raw bytes into 32-bit floating point将 4 个原始字节转换为 32 位浮点
【发布时间】:2014-10-11 01:50:08
【问题描述】:

我正在尝试从 eeprom 重新构造一个 32 位浮点值。

eeprom内存中的4个字节(0-4)是:B4 A2 91 4D

PC(VS Studio)将其正确重构为 3.054199 * 10^8(我知道的浮点值应该在那里)

现在我正在移动这个 eeprom 以从 8 位 Arduino 读取,所以不确定它是否是编译器/平台的东西,但是当我尝试将 4 个字节读入 32 位 dword,然后将其类型转换为一个浮点数,我得到的值甚至不接近。

假设使用标准的ansi-c编译器不能自动完成转换,如何手动将4个字节解析为浮点数?

【问题讨论】:

  • 听起来像endianness issue 但没有代码我们无法确定。在我对该问题的回答中,我链接到一个转换器,因此您可以尝试翻转这些值,看看是否可以重复您看到的结果以确认。
  • 我同意这可能是字节序。你在 Arduino 上得到大约 -3.0280572E-7 吗?
  • 有趣的是,当第一个字节被视为 MSB 时,编译器(浮点)转换产生:3.03055283E9.. 但是我确实记得最初看到 -3.02E-7 但不知道为什么我不这样做不再。当第一个字节被视为 LSB 时,编译器将转换为:1.30138995E9。
  • Here是如何在PHP中将字节数组转换为浮点数。

标签: c++ c floating-point floating-point-conversion


【解决方案1】:

最安全的方法是使用memcpy

uint32_t dword = 0x4D91A2B4;
float f;
memcpy(&f, &dw, 4);

演示:http://ideone.com/riDfFw

【讨论】:

  • 谢谢,效果很好!由于我的程序空间几乎已满,我将尝试添加一个自定义 memcpy 实现,以避免包含 stdlib.h...
  • @ben:我首先将 memcpy 原型复制到您的代码中...大多数编译器都识别名称 memcpy,并且当 size 参数固定且较小时,实际上不会调用函数。
【解决方案2】:

正如Shafik Yaghmour 在他的回答中提到的那样 - 这可能是一个endianness 问题,因为这是您在这种低级操作中可能遇到的唯一合乎逻辑的问题。而Shafiks answer在他所链接的问题中,基本上涵盖了处理此类问题的过程,我只是给您留下一些信息:

作为 Anduino 论坛上的 stated,Anduino 使用 Little Endian。如果你不确定你最终要处理的系统的字节序是什么,但想让你的代码半多平台,你可以在运行时用一个简单的代码 sn-p 检查字节序:

bool isBigEndian(){
   int number = 1;
   return (*(char*)&number != 1);
}

请注意 - 总而言之 - 这会消耗您的一些处理器时间并使您的程序运行速度变慢,虽然这几乎总是一件坏事,但您仍然可以使用它来查看应用程序的调试版本中的结果.

它的工作原理是它测试存储在&number 指向的地址处的int 的第一个字节。如果第一个字节不是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;
}

【讨论】:

  • 谢谢,endiness 确实有所作为,但无论我以哪种方式订购字节,浮点转换本身都没有给出正确的值。我选择了 Ben 的答案,因为它是第一个,但真的很喜欢你的代码!
【解决方案3】:

您需要在指针级别进行强制转换。

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]);

【讨论】:

  • 谢谢,我喜欢它的美丽!它似乎使用内置编译器转换而无需单独调用..它工作正常!我不使用它的唯一原因是因为严格的别名警告..即使我不确定这意味着什么我会相信比我更有经验的人在编程方面的话,它可能会导致意想不到的问题!
  • 这意味着优化器在技术上被允许在这里做可怕的事情,因为严格阅读标准称之为未定义行为。实际上,在主要系统中存在大量现有生产代码,如果优化器开始弄错的话,这些代码将会中断。
  • 我认为您可以通过使用 int 和 float 的并集来解决别名问题,而不是在这两种类型之间进行强制转换。
猜你喜欢
  • 1970-01-01
  • 2020-04-27
  • 2020-05-09
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
  • 2014-08-13
相关资源
最近更新 更多