Convertir 4.11 (integer.fraction) a float32

1

Estoy obteniendo un valor de un registro (SRCx_RATIO) de un DSP SHARC, la hoja de datos dice:

Estoy usando el siguiente código para convertir el valor:

 // Convert the sample rate ratio to a float from 4.11 format.
   int_part = (((src_reg  >> 14) & BIT_0) * TWO_POW_3) +
              (((src_reg  >> 13) & BIT_0) * TWO_POW_2) +
              (((src_reg  >> 12) & BIT_0) * TWO_POW_1) +
              (((src_reg  >> 11) & BIT_0) * TWO_POW_0);

   frac_part = (((src_reg  >> 10) & BIT_0) * TWO_POW_MINUS1) +
               (((src_reg  >>  9) & BIT_0) * TWO_POW_MINUS2) +
               (((src_reg  >>  8) & BIT_0) * TWO_POW_MINUS3) +
               (((src_reg  >>  7) & BIT_0) * TWO_POW_MINUS4) +
               (((src_reg  >>  6) & BIT_0) * TWO_POW_MINUS5) +
               (((src_reg  >>  5) & BIT_0) * TWO_POW_MINUS6) +
               (((src_reg  >>  4) & BIT_0) * TWO_POW_MINUS7) +
               (((src_reg  >>  3) & BIT_0) * TWO_POW_MINUS8) +
               (((src_reg  >>  2) & BIT_0) * TWO_POW_MINUS9) +
               (((src_reg  >>  1) & BIT_0) * TWO_POW_MINUS10) +
                ((src_reg         & BIT_0) * TWO_POW_MINUS11);

   // Return the value to be used.
   *ratio = (int_part + frac_part);

Las definiciones son:

// Power of 2 converstion used with 4.11 to float conversion.
#define TWO_POW_0       1.0F         // (2^0)
#define TWO_POW_1       2.0F         // (2^1)
#define TWO_POW_2       4.0F         // (2^2)
#define TWO_POW_3       8.0F         // (2^3)
#define TWO_POW_MINUS1  0.5F         // (2^-1)
#define TWO_POW_MINUS2  0.25F        // (2^-2)
#define TWO_POW_MINUS3  0.125F       // (2^-3)
#define TWO_POW_MINUS4  0.0625F      // (2^-4)
#define TWO_POW_MINUS5  0.03125      // (2^-5)
#define TWO_POW_MINUS6  0.015625F    // (2^-6)
#define TWO_POW_MINUS7  0.0078125F   // (2^-7)
#define TWO_POW_MINUS8  0.00390625F  // (2^-8)
#define TWO_POW_MINUS9  0.001953125F // (2^-9)
#define TWO_POW_MINUS10 0.000976563F // (2^-10)
#define TWO_POW_MINUS11 0.000488281F // (2^-11)

#define BIT_0  (1 << 0) 

¿Alguien sabe una mejor manera de obtener este valor?

    
pregunta user2982010

3 respuestas

1

Las otras respuestas ya mostraban cómo hacerlo a mano. La biblioteca matemática de C, por otro lado, tiene una función obsesiva y rara vez utilizada para hacer lo que necesitas:

float convert (uint32_t register_value)
{
  // extract field from register:
  uint32_t ratio = register_value & 0x7fff;

  // interpret as float with the exponent -11 applied:
  return ldexpf (ratio, -11);
}
    
respondido por el Nils Pipenbrinck
1

Lo estás pensando demasiado con toda la descomposición del valor en bits individuales. Tampoco es necesario dividir la parte fraccionaria de la parte entera.

Digamos que su valor Q4.11 representa el número
1010.01111001011 2 (10.47412109375 en decimal)
y se almacena como
101001111001011 2
en la memoria.

  • El primer paso es convertir directamente el entero en bruto a un punto flotante de 32 bits, sin tener en cuenta el punto decimal por completo:

    101001111001011 2 → 010001101 01001111001011 000000000 2

    que significa 1.01001111001011 2 * 2 14 , la representación en coma flotante del valor del entero original.

  • A continuación, divida el valor del punto flotante entre dos y la potencia del número de bits fraccionarios en el valor del punto fijo (2 11 en su caso)

    (1.01001111001011 2 * 2 14 ) / 2 11 = 1.01001111001011 2 * 2 3

    que es solo la representación de punto flotante de 1010.01111001011. Hecho.

Para hacer esto en C / C ++, solo necesita una línea de código:

float val = (float)src_reg / pow(2, 11);  

O incluso mejor

float val = (float)src_reg / (1UL << 11);  

que evita vincular innecesariamente las funciones matemáticas al reemplazar la exponenciación con turnos a la izquierda de 1.

    
respondido por el jms
0

Ya tienes todo en orden y valores adecuados. En este momento, es como si estuvieras apuntando a una bicicleta y diciendo "¡ Esto! ¡Es mi nuevo invento! Lo llamaré ... '¡Bicicleta!' ". Bueno ... No necesitas inventar cosas que ya existen, así que haz esto:

int_part = src_reg>>11;
frac_part = (src_reg&0x3FF)*pow(2,-11);
*ratio = (int_part + frac_part);

> > 11 es todo lo que necesitas hacer para moverlo al lugar correcto sin nada alrededor.

\ $ 3FF_ {16} = 1111111111_2 \ $, en palabras: es \ $ 10_ {10} \ $ unidades seguidas.

pow (2, -11) es \ $ 2 ^ {- 11} \ $, porque usé dos constantes en su entrada, el compilador lo convertirá en algo como tus otras constantes numéricas.

También podría usar / pow (2,11), pero luego usaría la división, no sé dónde se usará tu código, pero la multiplicación siempre es más rápida que la división. Por eso elegí * pow (2, -11).

Podría haber arruinado un número en algún lugar, tal vez debería ser pow (2, -10) o pow (2, -12), y tal vez debería ser src_reg > > 10 o src_reg > > 12. Siempre arruino esas cosas, así que si no obtienes la respuesta correcta, intenta un poco con ella y obtendrás la misma respuesta que obtienes ahora.

    
respondido por el Harry Svensson

Lea otras preguntas en las etiquetas