【问题标题】:How to get float value from uint32_t?如何从 uint32_t 获取浮点值?
【发布时间】:2019-12-07 10:01:32
【问题描述】:

我使用这部分代码从我的微控制器上的 OSC 消息中读取浮点值。但是我得到“取消引用类型双关指针将破坏严格别名规则 [-Wstrict-aliasing]”错误,并且 printf 上没有显示任何值。这个有什么解决方法吗?标记的结构为:

char *marker;  // the current read head
float tosc_getNextFloat(tosc_message *o) {
  // convert from big-endian (network btye order)
  const uint32_t i = ntohl(*((uint32_t *) o->marker));
  o->marker += 4;
  return *((float *) (&i));  <---- this line of code does the error
}

编辑:

所以,我使用功能通过 udp 从微控制器互联网芯片接收数据:

datasize_t recvfrom(uint8_t sn, uint8_t * buf, datasize_t len, uint8_t * addr, uint16_t *port, uint8_t *addrlen) //<- general 

然后我运行另一个函数来解析 osc 消息:

tosc_parseMessage(&A, (char*) buf, received_size); //<- how i wrote parameters

其中 A 是结构:

 typedef struct tosc_message {
    char *format;  // a pointer to the format field
    char *marker;  // the current read head
    char *buffer;  // the original message data (also points to the address)
    uint32_t len;  // length of the buffer data
} tosc_message;

而 tosc_parseMessage 是:

int tosc_parseMessage(tosc_message *o, char *buffer, const int len) {
  // NOTE(mhroth): if there's a comma in the address, that's weird
  int i = 0;
  while (buffer[i] != '\0') ++i; // find the null-terimated address
  while (buffer[i] != ',') ++i; // find the comma which starts the format string
  if (i >= len) return -1; // error while looking for format string
  // format string is null terminated
  o->format = buffer + i + 1; // format starts after comma

  while (i < len && buffer[i] != '\0') ++i;
  if (i == len) return -2; // format string not null terminated

  i = (i + 4) & ~0x3; // advance to the next multiple of 4 after trailing '\0'
  o->marker = buffer + i;

  o->buffer = buffer;
  o->len = len;

  return 0;
}

然后我用 :

打印出来
void tosc_printMessage(tosc_message *osc) {
  printf("[%i bytes] %s %s",
      osc->len, // the number of bytes in the OSC message
      tosc_getAddress(osc), // the OSC address string, e.g. "/button1"
      tosc_getFormat(osc)); // the OSC format string, e.g. "f"

  for (int i = 0; osc->format[i] != '\0'; i++) {
    switch (osc->format[i]) {
      case 'f': printf(" %g", tosc_getNextFloat(osc)); break;
      case 'd': printf(" %g", tosc_getNextDouble(osc)); break;
      case 'i': printf(" %d", tosc_getNextInt32(osc)); break;
      default: printf(" Unknown format: '%c'", osc->format[i]); break;
    }
  }
  printf("\n");
}

我的问题出在功能上:

float tosc_getNextFloat(tosc_message *o) {
  // convert from big-endian (network btye order)
  const uint32_t i = ntohl(*((uint32_t *) o->marker));
  o->marker += 4;
  return *((float *) (&i));  <---- this line of code does the error
}

我希望这能让您更好地了解问题...我不是熟练的程序员,所以我很感激任何帮助。这个“库”的完整代码可以在这里找到https://github.com/mhroth/tinyosc,我只是想在我的微控制器中实现它

【问题讨论】:

标签: c strict-aliasing type-punning


【解决方案1】:
float tosc_getNextFloat(tosc_message *o) {
  // convert from big-endian (network btye order)
  const uint32_t i = ntohl(*((uint32_t *) o->marker));
  o->marker += 4;

  float tmp = 0;
  memcpy((void *)&tmp, (void *)&i, sizeof(uint32_t));
  return tmp;
}

【讨论】:

  • 所以,输入可能是空的 :) 我正在考虑 'o' 指向的缓冲区。
  • 我应该得到如下值:0,56487 0,83412,我连接的程序正在发送它(我可以通过 osc 监视器看到)
  • 你现在得到了什么?总是0?检查缓冲区 *o - 它是如何填充的?数据是如何写入其中的?向我们展示代码。
  • 我已经设法通过 printf %c 我的缓冲区得到一些东西...要获得 0.59088 的值,我有下一个十六进制数字(逐字节)“?ETB C xBA”(这应该是 3F 17 43 e9,我查看了浮点到十六进制转换器).. 完整消息是:/composition/crossfader/phaseNULNULNUL,fNULNUL?ETBCxBANULNULNULNULNULNULNULNULNUL(如记事本++中所示)
  • 经过一番调查,它应该是 0.590877.. 所以浮点数的十六进制是“3f 17 43 ba”
【解决方案2】:

如果需要类型双关语,最好使用配置为支持它的编译器(在一些非商业设计的编译器上,但不是商业设计的编译器,意味着使用-fno-strict-aliasing)而不是跳过箍来容纳拒绝通过明显新派生的指针识别类型双关语的编译器编写者。

C 实现有时用于类型双关语有用的目的,有时用于不有用的目的。该标准的作者认识到编译器编写者应该比委员会更了解他们个人客户的需求,因此允许实现支持最​​适合其客户需求的任何构造组合。不知何故出现了一个神话,标准将其描述为“损坏的”程序,该程序依赖于实现来处理它们“以环境的文档化方式”,但任何这样的阅读都直接与标准作者声明的意图相矛盾。

【讨论】:

  • 不知何故出现了一个神话,标准将其描述为“损坏的”程序,该程序依赖于实现来处理它们“以环境的记录时尚特征” IMO 这更像是一种反应对于“它有效,所以它一定没问题”的编码人员,他们不知道他们正在违反规则,因为他们的平台让他们逃脱了它。只需谷歌“SIGBUS ARM”或“SIGBUS SPARC”,当他们得知return *((float *) (&amp;i)) 可以而且将会爆炸时,会感到有些惊愕和惊讶。
  • @AndrewHenle:依赖于“环境的记录行为特征”的代码只有在具有适当特征行为的环境中运行时才能按预期工作。另一方面,大多数代码只会被调用在具有某些特定已知特征的执行环境中运行。 C 语言流行和有用的原因之一是简单的实现可以让程序员利用程序员知道的目标环境的特征,而不必理解它们。
  • @AndrewHenle:从您的查询中查看初始命中表明代码试图从未对齐的地址加载浮点数。需要在不允许此类加载的平台上运行的代码需要在加载数据之前将数据复制到正确对齐的位置,但这并不意味着在访问对齐已经满足的内容时必须进行此类复制目标的要求。
  • @AndrewHenle:当标准规定在未能指定动作的行为和动作调用未定义行为的声明之间没有强调的区别时,在以下情况下应该如何解释该标准不会指定所有硬件上的一般行为类别的行为,但标准的部分内容与实现的文档相结合将描述该标准的某些其他部分以 UB 为特征的行为的行为。在没有标准的后半部分的情况下,行为将被定义..
  • ...尽管标准本身并没有定义它。一些编译器作者似乎认为“强调没有区别”意味着“UB 胜过一切”。我自己的解释是,在这种冲突的情况下,优先级需要由标准之外的东西来确定(例如,什么最适合客户,什么最适合不关心服务的假设编译器作者的突发奇想)客户等)您能否对标准的意图提供更合理的解释?
【解决方案3】:

毕竟这不是代码问题。是的,我仍然收到该警告(但代码有效)。实际问题出在 IDE 中。在构建器设置中的链接器设置中,需要添加:-u _printf_float。我花了一个月的时间才弄清楚发生了什么。谢谢大家的回答

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-13
    • 1970-01-01
    • 2016-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多