将 float 转换为 uint8 缓冲区

Convert float to uint8 buffer

A 有一个 float 变量,我需要将它的值作为字符存储在 uint8 缓冲区中。更确切地说,给定这样的东西

float f = 123.45
uint8_t buffer[11];
memset(buffer, 0x30, sizeof(buffer));   // I set it at 0x30 because it is character '0' 

鉴于此示例,我的缓冲区需要获取以下值:

0x30 0x30 0x30 0x30 0x30 0x31 0x32 0x33 0x2E 0x34 0x35   // 0x2E is the character '.'

不幸的是,我需要以这种方式将其与现有功能集成,因此无法解决 11 缓冲区的大小问题。

任何关于如何解决这个问题的建议都将不胜感激。

一个简单的方法是:

  1. 为缓冲区再分配一个元素(此处为buffer_buffer
  2. 通过snprintf()
  3. 将浮点数转换为字符串
  4. 复制转换结果到buffer
#include <stdio.h>
#include <string.h>
#include <inttypes.h>

int main(void) {
    float f = 123.45;
    uint8_t buffer[11];
    char buffer_buffer[12];
    snprintf(buffer_buffer, sizeof(buffer_buffer), "%011.2f", f);
    memcpy(buffer, buffer_buffer, sizeof(buffer));
    for (int i = 0; i < 11; i++) printf(" 0x%02X", buffer[i]);
    putchar('\n');
    return 0;
}

直接使用 snprintf()buffer 效果不佳,因为那里没有终止空字符的空间。

I need it's value to be stored in a uint8 buffer as characters.

使用sprintf()和朋友将float转换为字符串。

诀窍在于格式选择。如何在 11 个字节中容纳 float 的范围和精度?

"%f" 对大值失败。对于较小的值,信息会丢失,因为全部变为“0.0”`。

"%e""%g" 解决了 "% .3E" 的范围问题,因为它打印 sd.dddEsee[=18=]",但可能会失去精度可能需要更多(例如,常见 float 最多 9 位有效数字)。

"%a" 解决了 "% .3A" 的范围问题,因为打印 0Xsx.xxxPsbb[=22=],更好一点(如果可以丢弃前导“0X”),因为十六进制数字携带更多信息, 但指数可能会占用 3 位数。


自适应解决方案。尝试各种岁差和 return 最好的。 snprintf() 将 return [1...10] 成功用于 11 字节缓冲区。

 char *print_float(size_t sz, char buffer[sz], float f) {
   for (int p = sz - 1; p >= 0; p--)
     int length = snprintf(buffer, sz, "%.*g", p, f);
     if (length > 0 && (unsigned) length < size) {
       return buffer;
     }
   }
   buffer[0] = 0;
   return buffer;
}