Skip to main content

Float16 浮点数转换

本例程演示如何使用 librm 中的 Float16 功能进行半精度浮点数(FP16)和单精度浮点数(FP32)之间的转换。

Float16 使用 16 位存储,相比 Float32 可以节省 50% 的存储空间和传输带宽,且大部分情况下精度损失相较于映射到 uint16 的整个范围(0~65535)要小。

FP16 在和上位机通信的时候非常有用,比如一个姿态四元数是 4 个 fp32,转换成 fp16 后只需要 8 个字节,可以放在一个 CAN 帧里传输完。

基础概念

  • Float16 (FP16):16 位半精度浮点数

    • 符号位:1 位
    • 指数位:5 位(偏移量 15)
    • 尾数位:10 位
    • 表示范围:约 ±6.55×10⁴
    • 精度:约 3-4 位有效数字
  • Float32 (FP32):32 位单精度浮点数

    • 符号位:1 位
    • 指数位:8 位(偏移量 127)
    • 尾数位:23 位
    • 表示范围:约 ±3.4×10³⁸
    • 精度:约 6-7 位有效数字

Float32 转 Float16

将单精度浮点数压缩为半精度浮点数,用于节省存储或传输带宽。

#include <librm.hpp>

int main() {
float value = 3.14159f;

// 转换为 Float16
rm::modules::f16 half_value = rm::modules::F32ToF16(value);

// 现在可以存储或传输 half_value(仅占 2 字节)
return 0;
}

Float16 转 Float32

将半精度浮点数解压为单精度浮点数,用于计算。

#include <librm.hpp>

int main() {
rm::modules::f16 half_value = 0x4248; // Float16 表示的 3.14...

// 转换为 Float32
float value = rm::modules::F16ToF32(half_value);

// 现在可以用 value 进行正常的浮点运算
float result = value * 2.0f;

return 0;
}

特殊值处理

Float16 支持 IEEE 754 标准的特殊值。

#include <librm.hpp>
#include <cmath>

int main() {
// 正无穷
float inf = INFINITY;
rm::modules::f16 h_inf = rm::modules::F32ToF16(inf);
float inf_back = rm::modules::F16ToF32(h_inf); // 仍是 INFINITY

// 负无穷
float neg_inf = -INFINITY;
rm::modules::f16 h_neg_inf = rm::modules::F32ToF16(neg_inf);

// NaN(非数字)
float nan = NAN;
rm::modules::f16 h_nan = rm::modules::F32ToF16(nan);

// 零值(正零和负零)
float zero = 0.0f;
float neg_zero = -0.0f;
rm::modules::f16 h_zero = rm::modules::F32ToF16(zero);
rm::modules::f16 h_neg_zero = rm::modules::F32ToF16(neg_zero);

return 0;
}

精度损失

Float16 的精度和范围比 Float32 小,转换时需要注意。

#include <librm.hpp>
#include <cstdio>

int main() {
// 精度损失示例
float original = 3.1415926f; // 7 位精度
rm::modules::f16 half = rm::modules::F32ToF16(original);
float restored = rm::modules::F16ToF32(half); // 约 3.141(3-4 位精度)

printf("Original: %.7f\n", original);
printf("Restored: %.7f\n", restored);
printf("Error: %.7f\n", original - restored);

// 超出范围:自动变为无穷
float huge = 1e10f; // 远超 Float16 范围
rm::modules::f16 h_huge = rm::modules::F32ToF16(huge);
float back = rm::modules::F16ToF32(h_huge); // 变为 INFINITY

// 次正规数(subnormal)
float tiny = 1e-10f;
rm::modules::f16 h_tiny = rm::modules::F32ToF16(tiny);
// 可能变为 0 或次正规数

return 0;
}