2012-10-10 82 views
5

¿Cómo puedo convertir un integer en un half precision float (que se almacenará en una matriz unsigned char[2]). El rango a la entrada int será desde 1-65535. La precisión realmente no es una preocupación.Conversión int a flotación de 16 bits (coma flotante de precisión media) en C++

Estoy haciendo algo similar para convertir a 16bit int en un unsigned char[2], pero entiendo que no hay half precision float tipo de datos C++. Ejemplo de esto a continuación:

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

Tal vez rele vant: http://gamedev.stackexchange.com/a/17410/9333 – slaphappy

+0

Con la sugerencia de Goz tal vez de ayuda: [Conversión de punto flotante de 32 bits a 16 bits] (http://stackoverflow.com/q/ 1659440/237483) –

Respuesta

4

Es una cosa muy sencilla, toda la información que necesita está en Wikipedia . aplicación

muestra:

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

salida (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

sencillo: D ... – UmNyobe

+1

@UmNyobe sencillo si conoce suficientes matemáticas escolares. –

+0

Se debe tener en cuenta que este código tiene un comportamiento de redondeo anormal. En gran medida, trunca en lugar de redondea a la más cercana (que es más común), pero es anómala en el extremo superior. Las entradas mayores que el valor finito representable máximo se convierten a infinito, en lugar de truncarse como otras entradas, incluso si son solo un poco mayores que el máximo. Por ejemplo, 0xfff (4095) se convierte en 0x6bff (4094), pero 0xfff0 (65520) o 0xffe1 (65505) se convierte en 0x7c00 (infinito) en lugar de 0x7bff (65504). –

2

me hizo la pregunta de cómo convertir números en coma flotante de 32 bits de coma flotante de 16 bits.

Float32 to Float16

Así que desde que se podía convertir fácilmente el int a un flotador y luego usar la pregunta anterior para crear un flotador de 16 bits. Sugeriría que esto es probablemente mucho más fácil que pasar de int directamente al float de 16 bits. Efectivamente al convertir a flotación de 32 bits ha hecho la mayor parte del trabajo duro y luego solo necesita cambiar algunas partes.

Editar: Viendo la excelente respuesta de Alexey, creo que es muy probable que usar un hardware int para la conversión flotante y luego cambiarlo sea probablemente un poco más rápido que su método. Puede valer la pena perfilar ambos métodos y compararlos.

0

comentario siguiente pregunta @kbok He utilizado la primera parte de this answer para obtener el medio de flotación y después de obtener la matriz:

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