Float32到Float16

Goz*_*Goz 8 c floating-point

有人可以向我解释如何将32位浮点值转换为16位浮点值吗?

(s =符号e =指数,m =尾数)

如果32位浮点数是1s7e24m
而16位浮点数是1s5e10m

那么这么简单吗?

int     fltInt32;
short   fltInt16;
memcpy( &fltInt32, &flt, sizeof( float ) );

fltInt16 = (fltInt32 & 0x00FFFFFF) >> 14;
fltInt16 |= ((fltInt32 & 0x7f000000) >> 26) << 10;
fltInt16 |= ((fltInt32 & 0x80000000) >> 16);
Run Code Online (Sandbox Code Playgroud)

我假设它不是那么简单......所以有人能告诉我你需要做什么吗?

编辑:我看到我的指针转移错误了...所以这会更好吗?

fltInt16 =  (fltInt32 & 0x007FFFFF) >> 13;
fltInt16 |= (fltInt32 & 0x7c000000) >> 13;
fltInt16 |= (fltInt32 & 0x80000000) >> 16;
Run Code Online (Sandbox Code Playgroud)

我希望这是正确的.如果我遗漏了一些明显的话,我会道歉.它在星期五晚上几乎是午夜......所以我不是"完全"清醒;)

编辑2:哎呀.又把它弄错了.我想失去前3位而不是更低!那怎么样:

fltInt16 =  (fltInt32 & 0x007FFFFF) >> 13;
fltInt16 |= (fltInt32 & 0x0f800000) >> 13;
fltInt16 |= (fltInt32 & 0x80000000) >> 16;
Run Code Online (Sandbox Code Playgroud)

最终代码应为:

fltInt16    =  ((fltInt32 & 0x7fffffff) >> 13) - (0x38000000 >> 13);
fltInt16    |= ((fltInt32 & 0x80000000) >> 16);
Run Code Online (Sandbox Code Playgroud)

sam*_*var 5

指数需要不偏不倚,限制和重新定位.这是我使用的快速代码:

unsigned int fltInt32;
unsigned short fltInt16;

fltInt16 = (fltInt32 >> 31) << 5;
unsigned short tmp = (fltInt32 >> 23) & 0xff;
tmp = (tmp - 0x70) & ((unsigned int)((int)(0x70 - tmp) >> 4) >> 27);
fltInt16 = (fltInt16 | tmp) << 10;
fltInt16 |= (fltInt32 >> 13) & 0x3ff;
Run Code Online (Sandbox Code Playgroud)

使用指数的查找表,这个代码会更快,但我使用这个代码因为它很容易适应SIMD工作流程.

实施的局限性:

  • 无法在float16中表示的溢出值将给出未定义的值.
  • 下溢值将在零之间返回未定义的值,2^-152^-14不是零.
  • Denormals将给出未定义的值.

非常规小心.如果您的架构使用它们,它们可能会极大地降低您的程序速度.


Pas*_*uoq 4

float32 和 float16 表示形式中的指数可能有偏差,并且偏差不同。您需要对从 float32 表示形式获得的指数进行取消偏置以获得实际指数,然后针对 float16 表示形式对其进行偏置。

除了这个细节之外,我确实认为它就是这么简单,但我仍然时不时地对浮点表示感到惊讶。

编辑:

  1. 在处理指数时检查是否溢出。

  2. 您的算法稍微突然地截断尾数的最后一位,这可能是可以接受的,但您可能希望通过查看即将丢弃的位来实现舍入到最接近的位。“0...”-> 向下舍入,“100..001...”-> 向上舍入,“100..00”-> 舍入为偶数。