2012-10-10 12 views
5

Jak przekonwertować wartość integer na half precision float (która ma zostać zapisana w tablicy unsigned char[2]). Zakres do wejścia int będzie wynosił 1-65535. Precyzja naprawdę nie jest problemem.Konwertuj int na 16-bitowy zmiennoprzecinkowy (połowa zmiennoprzecinkowa) w C++

Robię coś podobnego do konwersji na 16bit int na unsigned char[2], ale rozumiem, że nie ma half precision float C++ typ danych. Przykład poniżej:

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

Może rele vant: http://gamedev.stackexchange.com/a/17410/9333 – slaphappy

+0

Dzięki wskazówce od Goz może pomóc: [Konwersja 32-bit do 16-bitowej zmiennoprzecinkowej] (http://stackoverflow.com/q/ 1659440/237483) –

Odpowiedz

4

To bardzo proste rzeczy, wszystkie informacje potrzebne jest w Wikipedia . Realizacja

Próbka:

#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; 
} 

wyjściowe (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 
+0

proste: D ... – UmNyobe

+1

@UmNyobe proste, jeśli znasz dość matematyki w szkole. –

+0

Należy zauważyć, że ten kod ma nieprawidłowe zachowanie podczas zaokrąglania. W dużej mierze jest on raczej przycięty niż zaokrągleń do najbliższego (co jest bardziej powszechne), ale w górnej części jest anomalny. Wejścia większe niż maksymalna reprezentowalna skończona wartość są konwertowane na nieskończoność, zamiast być obcięte jak inne wejścia, nawet jeśli są tylko nieznacznie większe niż maksimum. Na przykład 0xfff (4095) jest konwertowany na 0x6bff (4094), ale 0xfff0 (65520) lub 0xffe1 (65505) jest konwertowany na 0x7c00 (nieskończoność) zamiast 0x7bff (65504). –

2

Zadałem pytanie, jak przekonwertować 32-bitowe zmiennoprzecinkowe na 16-bitowy zmiennoprzecinkowy.

Float32 to Float16

Więc od tego można bardzo łatwo przekształcić int na float, a następnie użyć w powyższy pytanie do stworzenia 16-bit float. Sugerowałbym, że jest to prawdopodobnie łatwiejsze niż przejście od int bezpośrednio do 16-bitowego float. Skutecznie, przekształcając się w 32-bitowy float, wykonałeś większość prac, a następnie wystarczy przesunąć kilka bitów.

Edycja: Patrząc na doskonałą odpowiedź Alexeya, myślę, że bardzo prawdopodobne jest, że użycie sprzętowego int do przekształcenia typu float, a następnie przesunięcie go na drugą jest prawdopodobnie szybsze niż jego metoda. Może warto byłoby profilować obie metody i je porównywać.

0

Po @kbok komentarz zapytania Użyłem pierwszą część this answer dostać pół pływaka, a następnie uzyskać tablicy:

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