Convierta 4.11 (entero.fracción) a float32

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

RegistroEstoy 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 defines 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?

¿Qué es src_reg? Es imposible responder sin saber esto.
src_reg es el valor del registro SRCx_RATIO

Respuestas (3)

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

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

Estás pensando demasiado en esto con toda esa 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 sin formato a punto flotante de 32 bits, sin tener en cuenta el punto decimal por completo:

    101001111001011 2 → 010001101 01001111001011 000000000 2

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

  • A continuación, divida el valor de punto flotante por dos a la potencia del número de bits fraccionarios en el valor de 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 coma flotante de 1010.01111001011. Hecho.


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

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

O mejor

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

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

esto es mágico si funciona, estoy usando el compilador IAR en una máquina de 32 bits, todavía necesito entenderlo antes de poder poner cosas mágicas en el código. Gracias

Ya tienes todo en el orden correcto y los valores correctos. En este momento es como si estuvieras señalando una bicicleta y diciendo "¡ Este! ¡Es mi nuevo invento! Lo llamaré... '¡Bicicleta!' ". Bueno... No necesitas inventar cosas que ya existen, así que solo 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.

3 F F dieciséis = 1111111111 2 , en palabras: es 10 10 unos en fila.

pow(2,-11) es 2 11 , debido a que usé dos constantes en su entrada, el compilador lo convertirá en algo parecido a sus 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á su código, pero la multiplicación siempre es más rápida de realizar que la división. Por eso elegí *pow(2,-11).

Podría haber estropeado un número en alguna parte, 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, juega un poco y obtendrás la misma respuesta que estás obteniendo ahora.