2010-06-11 4 views
8

Czy ktoś może mi wyjaśnić, w jaki sposób przekonwertować 32-bitową wartość zmiennoprzecinkową na 16-bitową wartość zmiennoprzecinkową?Float32 do Float16

(s = znak e = m = wykładnik i mantysa)

Jeśli 32-bitowy pływak jest 1s7e24m
i 16-bitowy pływak jest 1s5e10m

Wtedy jest to tak proste, jak robi?

int  fltInt32; 
short fltInt16; 
memcpy(&fltInt32, &flt, sizeof(float)); 

fltInt16 = (fltInt32 & 0x00FFFFFF) >> 14; 
fltInt16 |= ((fltInt32 & 0x7f000000) >> 26) << 10; 
fltInt16 |= ((fltInt32 & 0x80000000) >> 16); 

Zakładam, że NIE JEST to takie proste ... więc czy ktoś może mi powiedzieć, co należy zrobić?

Edytuj: Kamera widzę, że moje przesunięcie wykładnicze jest nieprawidłowe ... więc TO BYŁO lepsze?

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

Mam nadzieję, że to prawda. Przepraszam, jeśli brakuje mi czegoś oczywistego, co zostało powiedziane. Jest prawie północ w piątkowy wieczór ... więc nie jestem "całkowicie" trzeźwy;)

Edytuj 2: Ooops. Powtórzył to jeszcze raz. Chcę stracić 3 najlepsze bity, a nie niższe! Tak jak o tym:

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

kod końcowy powinien być:

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

Myślę, że to było już zadane (i odpowiedziałem) tutaj: http://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion – humbagumba

+0

może to być takie proste, ale tracisz precyzję, chyba że float32 nie używa całej "precyzji", jaką ma ... w zasadzie dostajesz 5/7 bitów exp (bierzesz oczywiście te najbardziej znaczące) i 10/24 mantysy; te wskaźniki mówią, jak bardzo można stracić w konwersji. dokładnie tak, jak to się dzieje, jeśli chcesz dopasować 32-bitową liczbę całkowitą do 16-bitowej liczby całkowitej ... zakres liczb reprezentowalnych przez układ jest mniejszy; "przycinanie" mantysy zmniejsza "precyzję", a wykładnik także ogranicza zakres: 5 podpisanych bitów daje -16 do +15, przeciw -64/+ 63 (jeśli zrobiłem to dobrze ...: D późno to jest) – ShinTakezou

+0

@ShinTakezou: Na pewno nie można stracić 16-bitowych danych i NIE stracić precyzji? Float16 jest znacznie mniej precyzyjny, a więc automatycznie ma mniejszą precyzję ... czy nie rozumiem cię? – Goz

Odpowiedz

4

Wykładniki w float32 i float16 reprezentacje są prawdopodobnie stronniczy i tendencyjny inaczej. Musisz uwolnić wykładnik, który otrzymałeś od reprezentacji float32, aby uzyskać aktualny wykładnik, a następnie ustawić go dla reprezentacji float16.

Poza tymi szczegółami, myślę, że to jest tak proste, ale wciąż jestem zaskoczony przez reprezentacje zmiennoprzecinkowe od czasu do czasu.

EDIT:

  1. Sprawdź przepełnienia, gdy robi się coś z wykładników podczas gdy jesteś na to.

  2. Twój algorytm skraca trochę bity mantisa, co może być akceptowalne, ale możesz chcieć zaimplementować, powiedzmy, zaokrąglenie do najbliższego, patrząc na bity, które mają zostać odrzucone. "0 ..." -> zaokrąglenie w dół, "100..001 ..." -> zaokrąglenie w górę, "100..00" -> zaokrąglone do parzystego.

+0

32-bitowe liczby zmiennoprzecinkowe w standardzie IEEE754 mają 23 bity wykładniczki mantysy i 8 bitów wykładnika. – bbudge

+0

@bbudge ... Wystarczająco fair Próbowałem zrobić to z pamięci. Oderwałem się od tego, oczywiście;) – Goz

4

Wykładnik musi być bezstronna, zaciskane i rebiased. Jest to szybki kod używam:

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; 

Kod ten będzie jeszcze szybciej z tabeli odnośników dla wykładnika, ale używam tego jednego, ponieważ jest łatwo dostosować do obiegu SIMD.

Ograniczenia realizacji:

  • przepełnione wartościami, które nie mogą być reprezentowane w float16 dadzą wartości nieokreślone.
  • Niepełne wartości zwrócą niezdefiniowaną wartość między 2^-15 i 2^-14 zamiast zera.
  • Denormały podadzą niezdefiniowane wartości.

Należy zachować ostrożność w przypadku denormałów. Jeśli Twoja architektura je wykorzystuje, mogą znacznie spowolnić twój program.