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:
проверка на переполнение когда вы делаете что-то с экспонентами, пока вы на нем.
ваш алгоритм усекает последние биты мантисы немного резко, что может быть приемлемо, но вы можете захотеть реализовать, скажем, раунд к ближайшему, глядя на биты, которые собираются быть отброшены. "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. Если ваша архитектура использует их, они могут значительно замедлить вашу программу.