问题描述
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
缓冲区的大小。
任何有关如何解决此问题的建议将不胜感激。
解决方法
一个简单的方法是:
- 再分配一个元素的缓冲区(此处为
buffer_buffer
) - 通过
snprintf()
将浮点数转换为字符串 - 将转换结果复制到
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
效果不佳,因为那里没有终止空字符的空间。
我需要将它的值作为字符存储在 uint8 缓冲区中。
使用 sprintf()
和朋友将 float
转换为字符串。
诀窍是格式选择。如何在 11 个字节中容纳 float
的范围和精度?
"%f"
对于大值失败。对于较小的值,信息会丢失,因为全部变为“0.0”`。
"%e"
或 "%g"
解决了 "% .3E"
的范围问题,因为它打印 sd.dddEsee\0"
,但可能会失去精度,这可能需要更多(例如常见的 float
最多 9 位有效数字)。
"%a"
解决了 "% .3A"
的范围问题,因为它打印 0Xsx.xxxPsbb\0
,由于十六进制数字携带更多信息,所以会好一点(如果可以丢弃前导“0X”)指数可能会出现在 3 位数字上。
自适应解决方案。尝试各种岁差并返回最好的。 snprintf()
将在 11 字节缓冲区成功时返回 [1...10]。
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;
}