1 简介
在当今高性能计算和深度学习领域,半精度浮点数(FP16)因其存储空间小、计算效率高的特点而被广泛应用。然而,在某些场景下,如精度要求较高的科学计算或者需要与 FP32 数据格式兼容的情况,将 FP16 转换为 FP32 就显得尤为重要。本文将介绍一种基于 C++ 实现的 FP16 到 FP32 的转换算法,该算法不仅考虑了数值精度,还优化了计算效率,旨在为开发者提供一个高效、可靠的解决方案。
2 代码实现
FP16 转 FP32 一般用于深度学习量化模型的后处理中,这里可以参考了 Rockchip 提供的类型转换代码
#ifndef _RKNN_APP_TYPE_HALF_H_
#define _RKNN_APP_TYPE_HALF_H_
typedef unsigned short half;
typedef unsigned short ushort;
typedef unsigned int uint;
uint as_uint(const float x) {
return *(uint*)&x;
}
float as_float(const uint x) {
return *(float*)&x;
}
float half_to_float(half x) { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits
// printf("1\n");
const uint e = (x&0x7C00)>>10; // exponent
// printf("2\n");
const uint m = (x&0x03FF)<<13; // mantissa
// printf("3\n");
const uint v = as_uint((float)m)>>23; // evil log2 bit hack to count leading zeros in denormalized format
// printf("4\n");
return as_float((x&0x8000)<<16 | (e!=0)*((e+112)<<23|m) | ((e==0)&(m!=0))*((v-37)<<23|((m<<(150-v))&0x007FE000))); // sign : normalized : denormalized
}
typedef union suf32
{
int i;
unsigned u;
float f;
} suf32;
half float_to_half(float x)
{
suf32 in;
in.f = x;
unsigned sign = in.u & 0x80000000;
in.u ^= sign;
ushort w;
if (in.u >= 0x47800000)
w = (ushort)(in.u > 0x7f800000 ? 0x7e00 : 0x7c00);
else {
if (in.u < 0x38800000) {
in.f += 0.5f;
w = (ushort)(in.u - 0x3f000000);
} else {
unsigned t = in.u + 0xc8000fff;
w = (ushort)((t + ((in.u >> 13) & 1)) >> 13);
}
}
w = (ushort)(w | (sign >> 16));
return w;
}
void float_to_half_array(float *src, half *dst, int size)
{
for (int i = 0; i < size; i++)
{
dst[i] = float_to_half(src[i]);
}
}
void half_to_float_array(half *src, float *dst, int size)
{
for (int i = 0; i < size; i++)
{
dst[i] = half_to_float(src[i]);
}
}
#endif