我在 STM32 上将 float 转换为 str 时遇到问题, 我有两个简单的函数来打印 Int 和 Float Int 工作正常,但浮点打印导致程序崩溃
void LCD_PrintInt(int value) {
char str[8];
sprintf(str, "%d", value);
LCD_Print(str);
}
void LCD_PrintFloat(float value, uint8_t length) {
char str[length];
snprintf(str, length + 1, "%f", value);
LCD_Print(str);
}
LCD_PrintFloat(99.9, 4); <- crash
LCD_Print("99.9"); <- works fine
我正在使用 STM32CubeIDE 并设置 -u _printf_float 链接器标志 我究竟做错了什么?浮点运算(在STM32上)是正确的方法吗?
我做错了什么?
减少1
不要告诉
snprintf()
有比实际可用的空间更多的空间。正如OP所经历的那样,这会导致未定义的行为(UB)。
char str[length];
// snprintf(str, length + 1, "%f", value); // One too many
snprintf(str, length, "%f", value);
最好使用适合所有人的格式和缓冲区大小
float
。我推荐指数格式,例如:%a
、%g
或 %e
。"%e"
示例:
// - d . dddddddd e - E...E \0
#define FLT_TEXT_SIZE (1 + 1 + 1 + 8 + 1 + 1 + 5 + 1)
char str[FLT_TEXT_SIZE];
snprintf(str, sizeof str, "%.*e", FLT_DECIMAL_DIG-1, value);
我怀疑OP的
snprintf()
不能很好地处理缓冲区太小的情况。使用充足的缓冲区。
对于“%d”,您至少需要 12 个字符,仅允许 8 个字符。
在“%f”的情况下,你需要 10 个,但你只分配了 4 个,然后你告诉 snprintf 你已经分配了 5 个。
您需要 10 的原因是“99.900000”加上 1 作为空终止符(printf 默认使用 6 位小数)。如果你只想要一位小数,那么你需要使用“%.1f”。
无论你想要多少个地方,你都必须告诉函数有多少字节是真正可用的。当您为空终止符添加 1 时,您还必须将其添加到缓冲区的大小,而不仅仅是函数参数。
[编辑] 我错过了更大的问题是
%f
需要 double 类型的参数,但你传递的是浮点数。如果您使用 -Wall
打开警告,它会告诉您这一点。
TL;DR:使用 https://github.com/bofh453/ftoa-fast/ 将浮点数转换为字符串
以newlib为例:snprintf支持打印float。 sniprintf 不支持打印浮点型,但使用少 20 kbyte 的闪存。
我的建议是使用 ftoa() 将浮点数转换为字符串,并使用 sniprintf() 打印字符串。 ftoa 成本约为 1.5 kbyte 闪存;虽然还有很多,但是比 snprintf() 的 20 kbyte 膨胀有了巨大的改进。
还有一个优点:snprintf() 使用 double; ftoa() 使用 float()。特别是在小型 MCU 上,速度差异非常明显。 HTH.