将 IEEE-754 16 位浮点数打包为 16 位无符号整数,同时保持顺序

Packing an IEEE-754 16-bit float to a 16-bit unsigned integer while preserving order

提问人:Mike 提问时间:1/17/2023 最后编辑:Daniel W.Mike 更新时间:1/17/2023 访问量:227

问:

我有一个 IEEE-754 16 位浮点数,我想将其无损打包为 16 位无符号整数。当然,最简单的方法是打包它的字节,然后解压缩它,但问题是我需要在程序中比较 16 位整数(即大于、小于等)。所以我正在寻找 f16 和 u16 之间的同构来保持秩序。谁能建议一种算法来做到这一点?谢谢!

浮点 类型转换 比较 字节序 IEEE-754

评论

0赞 Daniel W. 1/17/2023
我允许自己调整您用于问题的标签,如果这不符合您的意图,请随时撤消。您能否举例说明您遇到的问题?字节序主要由较低级别的实现处理,除非您在不同体系结构之间交换原始数据。
0赞 jasonharper 1/17/2023
整数表示绝对必须是无符号的吗?IEE754 似乎与较长的标准浮点大小具有相同的基本结构,其特性是您可以简单地将位视为有符号整数并获得适当的比较行为。(好吧,除了 NaN,但不可能有行为类似于 NaN 的 int 值。
1赞 Steve Summit 1/17/2023
现有的表示非常非常接近。我认为您只需要 (a) 如果 MSbit 为 0,则不要管它,(b) 如果 MSbit 为 1,则将剩余的 15 位设置为 32768 - 剩余的位。(或者作为单行,。(x & 0x8000) ? (0x8000 | (0x8000 - (x & 0x7fff))) : x
1赞 Steve Summit 1/17/2023
但我差了一个。从 32767 中减去,或 .0x7fff
1赞 Steve Summit 1/17/2023
如果你的数字总是正数,你可以跳过所有这些!你只需要做任何大惊小怪的事情,就可以让负数正确地比较为(有符号的)整数。(要让它们正确地作为无符号进行比较,需要更多的工作,即切换位。起初我忽略了这一点。最终答案 .而且,仍然有一个错误:这比较,当它们应该相等时。0x8000(x & 0x8000) ? 0x7fff - (x & 0x7fff) : 0x80000 | x+0 > -0

答:

3赞 chux - Reinstate Monica 1/17/2023 #1

要使用整数数学来维护 float16,请将数据视为使用符号大小编码的有符号整数。<, ==, >

使用 和 执行此操作以获得正确的代码(因为并非所有代码都可用),然后针对 16 位进行调整。float(u)int32_tfloat16_t

将负值否定为正值,并将 MSBit 设置为正值。

确保 +0.0 和 -0.0 转换为相同的值。

// Assumes same endian for FP and integers
#include <float.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>

// Assumes same endian for FP and integers
unsigned float_to_sequence(float f) {
  union {
    float f;
    int32_t i;
    uint32_t u;
  } x = {.f = f};
  if (x.i < 0) {
    x.u = -x.u;
  } else {
    x.u |= 0x80000000;
  }
  return x.u;
}

测试

void test(float f) {
  printf("%+-20a %+-18.9e ", f, f);
  printf("0x%08X\n", float_to_sequence(f));
}

int main(void) {
  float f[] = {-INFINITY, -FLT_MAX, -1.0, -FLT_TRUE_MIN, -0.0, //
      0.0, FLT_TRUE_MIN, 1.0, FLT_MAX, INFINITY};
  size_t n = sizeof f / sizeof f[0];
  for (size_t i = 0; i < n; i++) {
    test(f[i]);
  }
}

输出

-inf                 -inf               0x00800000
-0x1.fffffep+127     -3.402823466e+38   0x00800001
-0x1p+0              -1.000000000e+00   0x40800000
-0x1p-149            -1.401298464e-45   0x7FFFFFFF
-0x0p+0              -0.000000000e+00   0x80000000
+0x0p+0              +0.000000000e+00   0x80000000
+0x1p-149            +1.401298464e-45   0x80000001
+0x1p+0              +1.000000000e+00   0xBF800000
+0x1.fffffep+127     +3.402823466e+38   0xFF7FFFFF
+inf                 +inf               0xFF800000

转换是一一,除了 +0.0 和 -0.0 都转换为相同的值 - 它应该这样做。

对于 16 位单行:uint16_t y = (x & 0x8000) ? -x : (x | 0x8000);