Arduino C++ float变量转十六进制结果与g++编译输出不符问题求解
输出结果差异的原因
二者运行环境的基础数据类型长度不同,同时原有写法本身存在未定义行为:
- PC端g++编译运行的x86/x86_64架构下,
unsigned int长度为4字节,和单精度float的长度完全一致,因此你将float指针强转为unsigned int*解引用时,刚好可以读取到float完整的4字节二进制内容。 - Arduino常用的AVR架构下,
unsigned int长度仅为2字节,强转解引用只能读取到float内存空间中低地址的2字节内容,对应3.141592654的IEEE754编码低16位为0x0fdb,加上%x格式化不会输出前导零,最终只打印出fdb,高16位的0x4049完全没有被读取。 - 直接对不同类型的指针做强制解引用违反C/C++的严格别名规则,属于未定义行为,即使PC端运行符合预期也只是巧合,不具备通用性。
可靠的float转十六进制实现方案
推荐两种兼容性高、不存在未定义行为的实现方式:
方案1:使用union做类型双关
该方案符合C语言标准,C++11及之后版本也明确支持,是嵌入式场景下的常用写法:
union FloatHexConverter { float float_val; uint32_t uint_val; }; void setup() { Serial.begin(9600); FloatHexConverter converter; converter.float_val = 3.141592654; // 直接通过串口输出十六进制 Serial.println(converter.uint_val, HEX); // 如果需要存入字符数组,使用%lx格式化4字节无符号整数,补前导零保证输出8位 char data[9]; sprintf(data, "%08lx", converter.uint_val); Serial.println(data); } void loop() { }
uint32_t是固定4字节长度的无符号整数类型,不会随架构变化出现长度不匹配的问题。
方案2:逐字节读取拼接
完全规避类型转换风险,适配所有架构,还可以灵活调整字节输出顺序适配大小端需求:
void setup() { Serial.begin(9600); float a = 3.141592654; // 用uint8_t指针读取float的每个字节 uint8_t *byte_ptr = (uint8_t *)&a; char data[9]; // 小端架构下按大端顺序(人类常用的十六进制显示顺序)拼接,需要小端输出可调整下标顺序为0、1、2、3 sprintf(data, "%02x%02x%02x%02x", byte_ptr[3], byte_ptr[2], byte_ptr[1], byte_ptr[0]); Serial.println(data); } void loop() { }
内容的提问来源于stack exchange,提问作者Łukasz Piórecki
相关产品推荐
相关产品推荐

