目录
[方法一:使用 union](#方法一:使用 union)
[方法二:使用 memcpy](#方法二:使用 memcpy)
[使用 union 方法](#使用 union 方法)
在 STM32H7 这样的嵌入式系统中,将四个 8 位无符号数据(uint8_t
)拼接成一个 32 位的 float
数据,通常需要考虑字节顺序(小端 或大端 )。STM32 默认使用小端(Little Endian)存储方式,即最低有效字节(LSB)存储在低地址,高位在高地址。
以下是几种实现方法,确保在拼接时正确处理字节顺序:
方法一:使用 union
union
允许不同类型的数据共享同一块内存区域。通过 union
,可以将四个字节赋值给 uint8_t
数组,然后直接读取为 float
类型。
#include <stdint.h>
#include <stdio.h>
// 定义一个联合体,包含一个 float 和一个 4 字节的数组
typedef union {
float f;
uint8_t bytes[4];
} FloatUnion;
// 函数:将四个字节拼接成 float
float bytesToFloat(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3) {
FloatUnion fu;
fu.bytes[0] = b0; // 低字节
fu.bytes[1] = b1;
fu.bytes[2] = b2;
fu.bytes[3] = b3; // 高字节
return fu.f;
}
int main(void) {
uint8_t byte0 = 0x00;
uint8_t byte1 = 0x00;
uint8_t byte2 = 0x80;
uint8_t byte3 = 0x3F; // 代表 float 1.0 的 IEEE 754 编码
float result = bytesToFloat(byte0, byte1, byte2, byte3);
printf("Float value: %f\n", result); // 输出应为 1.000000
return 0;
}
输出:
Float value: 1.000000
解释
- 联合体
FloatUnion
:包含一个float
和一个uint8_t
数组,二者共享同一内存。 - 函数
bytesToFloat
:将四个字节依次赋值给bytes
数组,然后通过fu.f
获取对应的float
值。 - 字节顺序 :假设输入字节按小端顺序(即最低有效字节在前),上述代码会正确地将其转换为
float
。
方法二:使用 memcpy
memcpy
可以安全地将字节数据复制到 float
类型的变量中,避免了潜在的严格别名规则(Strict Aliasing Rule)问题。
#include <stdint.h>
#include <stdio.h>
#include <string.h>
// 函数:将四个字节拼接成 float
float bytesToFloat(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3) {
uint32_t temp = ((uint32_t)b3 << 24) | ((uint32_t)b2 << 16) | ((uint32_t)b1 << 8) | b0;
float f;
memcpy(&f, &temp, sizeof(f));
return f;
}
int main(void) {
uint8_t byte0 = 0x00;
uint8_t byte1 = 0x00;
uint8_t byte2 = 0x80;
uint8_t byte3 = 0x3F; // 代表 float 1.0 的 IEEE 754 编码
float result = bytesToFloat(byte0, byte1, byte2, byte3);
printf("Float value: %f\n", result); // 输出应为 1.000000
return 0;
}
输出:
Float value: 1.000000
解释
- 拼接字节 :将四个字节按小端顺序拼接成一个 32 位无符号整数
temp
。 memcpy
:将temp
的内容复制到float
变量f
中。- 优点:避免了通过指针类型转换可能引发的未定义行为。
方法三:直接指针类型转换(不推荐)
虽然可以通过指针类型转换实现,但这种方法可能违反 C 语言的严格别名规则,导致未定义行为。建议优先使用 union
或 memcpy
。
#include <stdint.h>
#include <stdio.h>
// 函数:将四个字节拼接成 float
float bytesToFloat(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3) {
uint32_t temp = ((uint32_t)b3 << 24) | ((uint32_t)b2 << 16) | ((uint32_t)b1 << 8) | b0;
return *(float*)&temp; // 可能导致未定义行为
}
int main(void) {
uint8_t byte0 = 0x00;
uint8_t byte1 = 0x00;
uint8_t byte2 = 0x80;
uint8_t byte3 = 0x3F; // 代表 float 1.0 的 IEEE 754 编码
float result = bytesToFloat(byte0, byte1, byte2, byte3);
printf("Float value: %f\n", result); // 输出应为 1.000000
return 0;
}
注意:这种方法不推荐使用,因为它可能在某些编译器或优化级别下引发问题。
综合推荐
使用 union
方法
union
方法简洁且安全,适合嵌入式系统中对性能和资源有严格要求的场景。以下是完整示例:
#include <stdint.h>
#include <stdio.h>
// 定义联合体
typedef union {
float f;
uint8_t bytes[4];
} FloatUnion;
// 将四个字节拼接成 float
float bytesToFloat(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3) {
FloatUnion fu;
fu.bytes[0] = b0; // LSB
fu.bytes[1] = b1;
fu.bytes[2] = b2;
fu.bytes[3] = b3; // MSB
return fu.f;
}
int main(void) {
// 示例字节,表示 float 1.0
uint8_t byte0 = 0x00;
uint8_t byte1 = 0x00;
uint8_t byte2 = 0x80;
uint8_t byte3 = 0x3F;
float result = bytesToFloat(byte0, byte1, byte2, byte3);
printf("Float value: %f\n", result); // 应输出 1.000000
return 0;
}
注意事项
- 字节顺序:确保输入字节按 STM32 的小端顺序排列。如果字节顺序不同,需要调整字节的赋值顺序。
- 浮点数表示:确保输入字节正确表示 IEEE 754 浮点数格式。
- 内存对齐 :在嵌入式系统中,访问未对齐的内存可能导致性能下降或异常。使用
union
或memcpy
通常能避免此问题。
验证代码
编译并运行上述代码,可以验证拼接结果是否正确。例如,四个字节 0x00, 0x00, 0x80, 0x3F
应对应 float
值 1.0
。
Float value: 1.000000
这样,你就成功地将四个 8 位无符号数据拼接成一个 32 位的 float
数据了。