【问题标题】:Convert int to 16bit float (half precision floating point) in c++在 C++ 中将 int 转换为 16 位浮点数(半精度浮点数)
【发布时间】:2012-10-10 10:39:29
【问题描述】:

如何将integer 转换为half precision float(将存储到数组unsigned char[2] 中)。输入 int 的范围为 1-65535。精度真的不是问题。

我正在做类似的事情来将16bit int 转换为unsigned char[2],但我知道没有half precision float C++ 数据类型。以下示例:

int16_t position16int = (int16_t)data;
memcpy(&dataArray, &position16int, 2);

【问题讨论】:

标签: c++ c floating-point


【解决方案1】:

这是一件非常简单的事情,您需要的所有信息都在Wikipedia。

示例实现:

#include <stdio.h>

unsigned int2hfloat(int x)
{
  unsigned sign = x < 0;
  unsigned absx = ((unsigned)x ^ -sign) + sign; // safe abs(x)
  unsigned tmp = absx, manbits = 0;
  int exp = 0, truncated = 0;

  // calculate the number of bits needed for the mantissa
  while (tmp)
  {
    tmp >>= 1;
    manbits++;
  }

  // half-precision floats have 11 bits in the mantissa.
  // truncate the excess or insert the lacking 0s until there are 11.
  if (manbits)
  {
    exp = 10; // exp bias because 1.0 is at bit position 10
    while (manbits > 11)
    {
      truncated |= absx & 1;
      absx >>= 1;
      manbits--;
      exp++;
    }
    while (manbits < 11)
    {
      absx <<= 1;
      manbits++;
      exp--;
    }
  }

  if (exp + truncated > 15)
  {
    // absx was too big, force it to +/- infinity
    exp = 31; // special infinity value
    absx = 0;
  }
  else if (manbits)
  {
    // normal case, absx > 0
    exp += 15; // bias the exponent
  }

  return (sign << 15) | ((unsigned)exp << 10) | (absx & ((1u<<10)-1));
}

int main(void)
{
  printf(" 0: 0x%04X\n", int2hfloat(0));
  printf("-1: 0x%04X\n", int2hfloat(-1));
  printf("+1: 0x%04X\n", int2hfloat(+1));
  printf("-2: 0x%04X\n", int2hfloat(-2));
  printf("+2: 0x%04X\n", int2hfloat(+2));
  printf("-3: 0x%04X\n", int2hfloat(-3));
  printf("+3: 0x%04X\n", int2hfloat(+3));
  printf("-2047: 0x%04X\n", int2hfloat(-2047));
  printf("+2047: 0x%04X\n", int2hfloat(+2047));
  printf("-2048: 0x%04X\n", int2hfloat(-2048));
  printf("+2048: 0x%04X\n", int2hfloat(+2048));
  printf("-2049: 0x%04X\n", int2hfloat(-2049)); // first inexact integer
  printf("+2049: 0x%04X\n", int2hfloat(+2049));
  printf("-2050: 0x%04X\n", int2hfloat(-2050));
  printf("+2050: 0x%04X\n", int2hfloat(+2050));
  printf("-32752: 0x%04X\n", int2hfloat(-32752));
  printf("+32752: 0x%04X\n", int2hfloat(+32752));
  printf("-32768: 0x%04X\n", int2hfloat(-32768));
  printf("+32768: 0x%04X\n", int2hfloat(+32768));
  printf("-65504: 0x%04X\n", int2hfloat(-65504)); // legal maximum
  printf("+65504: 0x%04X\n", int2hfloat(+65504));
  printf("-65505: 0x%04X\n", int2hfloat(-65505)); // infinity from here on
  printf("+65505: 0x%04X\n", int2hfloat(+65505));
  printf("-65535: 0x%04X\n", int2hfloat(-65535));
  printf("+65535: 0x%04X\n", int2hfloat(+65535));
  return 0;
}

输出(ideone):

 0: 0x0000
-1: 0xBC00
+1: 0x3C00
-2: 0xC000
+2: 0x4000
-3: 0xC200
+3: 0x4200
-2047: 0xE7FF
+2047: 0x67FF
-2048: 0xE800
+2048: 0x6800
-2049: 0xE800
+2049: 0x6800
-2050: 0xE801
+2050: 0x6801
-32752: 0xF7FF
+32752: 0x77FF
-32768: 0xF800
+32768: 0x7800
-65504: 0xFBFF
+65504: 0x7BFF
-65505: 0xFC00
+65505: 0x7C00
-65535: 0xFC00
+65535: 0x7C00

【讨论】:

  • @UmNyobe 如果你对学校数学有足够的了解,那就直截了当。
  • 需要注意的是这段代码有异常的舍入行为。很大程度上,它截断而不是四舍五入到最近(这更常见),但在上端是异常的。大于最大可表示有限值的输入将转换为无穷大,而不是像其他输入一样被截断,即使它们仅略大于最大值。例如 0xfff (4095) 被转换为 0x6bff (4094),但 0xfff0 (65520) 或 0xffe1 (65505) 被转换为 0x7c00 (infinity) 而不是 0x7bff (65504)。
  • @EricPostpischil 你是对的。但这是我对Precision is really not a concern的解释。
  • @EricPostpischil 如何使用四舍五入法实现相同的功能?
【解决方案2】:

我问了如何将32位浮点数转换为16位浮点数的问题。

Float32 to Float16

因此,您可以很容易地将 int 转换为浮点数,然后使用上面的问题创建一个 16 位浮点数。我建议这可能比从 int 直接转到 16 位浮点数要容易得多。实际上,通过转换为 32 位浮点数,您已经完成了大部分工作,然后您只需要移动几位即可。

编辑:查看 Alexey 的出色答案,我认为使用硬件 int 进行浮点转换然后对其进行位移很可能比他的方法快一点。可能值得对这两种方法进行分析并进行比较。

【讨论】:

    【解决方案3】:

    在@kbok 问题评论之后,我使用this answer 的第一部分来获取半浮点数,然后获取数组:

    uint16_t position16float = float_to_half_branch(data);
    memcpy(&dataArray, &position16float, 2);
    

    【讨论】:

      【解决方案4】:
      猜你喜欢
      • 2020-02-03
      • 1970-01-01
      • 2011-11-17
      • 2013-04-16
      • 1970-01-01
      • 1970-01-01
      • 2014-09-03
      • 2015-06-12
      • 2011-10-25
      相关资源
      最近更新 更多