Float32 в Float16

может кто-нибудь объяснить мне, как я преобразовываю 32-битное значение с плавающей запятой в 16-битное значение с плавающей запятой?

(s = знак e = показатель и m = мантисса)

Если 32-битный float равен 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);

Я предполагаю, что это не так просто ... кто-нибудь может сказать мне, что вам нужно сделать?

Edit: я вижу, что у меня неправильный сдвиг экспоненты ... так это быть лучше?

fltInt16 =  (fltInt32 & 0x007FFFFF) >> 13;
fltInt16 |= (fltInt32 & 0x7c000000) >> 13;
fltInt16 |= (fltInt32 & 0x80000000) >> 16;

Я надеюсь, что это правильно. Извините, если я упустил что-то очевидное, что было сказано. Почти полночь в пятницу вечером ... так что я не" совсем " трезв ;)

Edit 2: Ooops. Опять все испортил. Я хочу потерять верхние 3 бита, не ниже! Так как насчет этого:

fltInt16 =  (fltInt32 & 0x007FFFFF) >> 13;
fltInt16 |= (fltInt32 & 0x0f800000) >> 13;
fltInt16 |= (fltInt32 & 0x80000000) >> 16;

окончательный код должен быть:

fltInt16    =  ((fltInt32 & 0x7fffffff) >> 13) - (0x38000000 >> 13);
fltInt16    |= ((fltInt32 & 0x80000000) >> 16);

3 ответов


показатели в ваших представлениях float32 и float16, вероятно, предвзяты и предвзяты по-разному. Вам нужно unbias показатель, который вы получили из представления float32, чтобы получить фактический показатель, а затем смещать его для представления float16.

помимо этой детали, я думаю, что это так просто, но я все еще удивляюсь представлениям с плавающей запятой время от времени.

EDIT:

  1. проверка на переполнение когда вы делаете что-то с экспонентами, пока вы на нем.

  2. ваш алгоритм усекает последние биты мантисы немного резко, что может быть приемлемо, но вы можете захотеть реализовать, скажем, раунд к ближайшему, глядя на биты, которые собираются быть отброшены. "0..."-> округлить, "100..001..."-> сгонять, "100..00" -> круглые даже.


показатель должен быть непредвзятым, зажимается и rebiased. Это быстрый код, который я использую:

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;

этот код будет еще быстрее с таблицей поиска для экспоненты, но я использую этот, потому что он легко адаптируется к рабочему процессу SIMD.

ограничения реализации:

  • переполнение значений, которые не могут быть представлены в float16, даст неопределенные значения.
  • значения Underflowing вернут неопределенное значение между 2^-15 и 2^-14 вместо нуля.
  • Denormals даст неопределенные значения.

будьте осторожны с denormals. Если ваша архитектура использует их, они могут значительно замедлить вашу программу.


вот ссылка на статью о IEEE754, которая дает битовые макеты и предубеждения.

http://en.wikipedia.org/wiki/IEEE_754-2008