Por favor, dígame cómo manejan los MCUs los números decimales como '23.3', '3.24', etc. ¿Cómo se almacenan en un registro de memoria? Sé que tengo que usar el tipo de datos float al manejar estos números durante la programación. Pero en realidad lo que está sucediendo dentro de un MCU, mientras que el manejo de estos tipos. También dígame cómo los MCUs sin unidad FPU manejan el tipo de datos Float.
Respuestas
¿Demasiados anuncios?Los números dentro de los típicos microcontroladores no tienen puntos decimales. Son enteros binarios. No hay decimales dentro de la máquina. El compilador o el ensamblador pueden permitirte especificar las constantes de esa manera, pero se convierten a binario antes de que la máquina las vea.
Sin embargo, puedes decidir las unidades que quieras para los valores enteros. Por ejemplo, suponga que quiere representar dólares dentro de un micro. No puede hacer nativamente $3.21, pero podría hacer 321 centavos. El micro sólo opera con el valor 321, pero usted sabe que representa unidades de 1/100 dólares.
Éste es sólo un ejemplo para ilustrar el concepto de unidades arbitrarias. A menudo los números se representan con varios bits de fracción binaria. Eso es lo mismo que decir que cada cuenta representa un valor de 2 -N donde N es el número de bits de la fracción. Esta representación se llama "punto fijo". Usted decide por adelantado cuánta resolución necesita, y pretende que hay suficientes bits a la derecha del punto binario imaginado para soportar esa resolución. Por ejemplo, digamos que necesitas representar algo con una resolución de 1/100 como mínimo. En ese caso, utilizarías al menos 7 bits de fracción, ya que 2 7 \= 128. Esto le dará una resolución de 1/128.
La máquina no tiene ni idea de lo que está pasando. Sumará y restará estos números como enteros ordinarios, pero todo sigue funcionando. La cosa se complica un poco cuando se multiplican y dividen valores de coma fija. El producto de dos valores de punto fijo con N bits de fracción tendrá 2N bits de fracción. A veces sólo hay que tener en cuenta el hecho de que el nuevo número tiene 2N bits de fracción, o a veces se puede desplazar a la derecha por N bits para volver a la misma representación que antes.
La coma flotante es lo mismo, pero el número de bits de la fracción se almacena junto con la parte entera para que este ajuste pueda realizarse en tiempo de ejecución. Realizar operaciones matemáticas con números de coma flotante puede llevar un montón de ciclos. El hardware de punto flotante hace todo esto por ti para que las operaciones se completen rápidamente. Sin embargo, las mismas manipulaciones pueden realizarse también en el software. No hay ninguna razón por la que no se pueda escribir una subrutina para sumar dos números de punto flotante, sólo que tardaría mucho más que el hardware dedicado a hacer lo mismo.
He definido un formato de punto flotante de 3 bytes para PICs de 8 bits y he escrito un montón de rutinas para manipularlos. Los microcontroladores suelen manejar valores del mundo real con 10 o 12 bits de precisión como máximo. Mi formato de punto flotante utiliza 16 bits de precisión, lo que es suficiente para varios cálculos intermedios.
También tengo un formato de 32 bits para los PICs de 16 bits. Esto utiliza una palabra de 16 bits para la mantisa, lo que acelera los cálculos ya que estos PICs pueden operar con 16 bits a la vez.
Estas rutinas están incluidas en mi Lanzamiento de las herramientas de desarrollo PIC . Después de la instalación, busque los archivos con "fp24" en su nombre en el directorio SOURCE > PIC, y "fp32f" en el directorio SOURCE > DSPIC.
Aritmética de punto fijo se suele utilizar para realizar cálculos fraccionarios en los MCU.
El truco consiste en decir que (por ejemplo), los 16 bits superiores de un uint32_t
están antes del punto decimal y los 16 inferiores están después, es decir, el entero almacenado está en 1/2^16. Con algunas pequeñas salvedades, la aritmética regular "simplemente funciona".
Aquí hay un resumen .
A menos que tu MCU sea un DSP con multiplicador de punto flotante, todo se almacena como números de 16 bits (u 8 o 32 dependiendo de tu plataforma). Eso es todo lo que la MCU conoce.
Por encima de esto tienes tu código "C" y el compilador C. El compilador "conoce" varios tipos de datos como char, int's, uint's, floats, doubles, etc.
La representación más común de los flotadores en el hardware es con un formato IEEE. Éste separa la mantisa del exponente y utiliza dos palabras de 16 bits para almacenar la información. Consulta este artículo de la wiki sobre los formatos numéricos IEEE.
Así que es el compilador el que sabe dónde está la mantisa y el exponente y le aplica las matemáticas. ¿Recuerdas lo que aprendiste sobre los logaritmos? ¿Cómo facilitaron las matemáticas añadiendo potencias cuando querías multiplicar? Pues el compilador de C hace algo similar con los exponentes y multiplica la mantisa para calcular la respuesta. Así que para una multiplicación en coma flotante el compilador creará un código ensamblador que suma los exponentes y realiza la multiplicación de la mantisa.
el MCU no sabe nada del número!!! sólo lo que se le dice que haga, cargar una memoria en un registro, añadir una memoria al registro y poner la bandera de acarreo si es necesario, y así sucesivamente hasta completar la multiplicación.
Es el compilador de C y su código el que "abstrae" el concepto de números, puntos decimales y demás del MCU.
Por otro lado, algunos lenguajes también soportan el tipo de datos "decimal", que es útil para los sistemas financieros - no es común en las plataformas embebidas, ya que los flotadores utilizan menos memoria y tienen un rendimiento más eficiente.
De la misma manera que los procesadores completos sin fpu (la mayoría de los ARM, por ejemplo) manejan el punto flotante. Con una fpu de software. Hay una biblioteca que realiza las operaciones matemáticas/de bits. Si recuerdas haber hecho sumas, multiplicaciones, etc. en la escuela primaria con lápiz y papel, no cambió mucho el día que pasaste de números enteros a números con punto decimal. Las matemáticas se hacían de la misma manera, sólo había que ajustar los números para que los puntos decimales se alinearan antes de empezar (sumas y restas) o después de terminar (multiplicaciones o divisiones). Los fpus duros y blandos no son diferentes, ajustan los bits antes y después de la operación, pero la operación es básicamente un número entero.
No recuerdo exactamente donde encontrarlo ahora, pero texas instruments tenía un documento muy bueno relacionado con sus productos DSP. Explicaba su formato de punto flotante y llegaba a explicar cómo funcionaban las operaciones. Su formato no tiene redondeo y denormales e infinito y nans de señalización como IEEE, por lo que es mucho más fácil de entender, así como significativamente más rápido que los formatos IEEE. Una vez que veas ese formato en acción habrás dado el primer paso hacia el formato IEEE. El redondeo requiere un poco de explicación y reflexión, pero el resto, los fundamentos del signo, el exponente y la mantisa son los mismos.
Es muy costoso, en cuanto a recursos (memoria, flash, ciclos de la cpu) utilizar librerías de flotación suave y yo lo desaconsejaría en un sistema embebido o en un microcontrolador. 12,3 y 24,5 son bastante fáciles de manejar como los enteros 123 y 245, por ejemplo, siempre y cuando usted recuerde o tal vez si se asegura de que todas sus matemáticas asociadas entienden que los números son todos multiplicados por diez y si / cuando se muestra a un usuario en esa conversión se añade el punto decimal. Ahorra toneladas de código y rendimiento. Por supuesto, la división de enteros es una cosa mala con los microcontroladores y sistemas embebidos, así como la mayoría de los procesadores no tienen una instrucción de división, y que incluye la división por 10 para convertir a decimal para mostrar al usuario. Y la misma respuesta, la división que obtienes de tu código C se hace usando una biblioteca. Así que la división en coma flotante en estos sistemas es cara al cuadrado.
Los flotadores se almacenan en formato binario de 32 bits y el 1er bit es para indicar si el flotador es un número pos/neg, los siguientes 8 bits son el exponente -127, luego hay 23 bits que son el número completo incluyendo el decimal, es decir:
1 00010001 00010001000000000000000
Entonces 1 es que es negativo, los siguientes 8 bits son para indicar el exponente en este caso:
0001 0001 = 17 (17-127 = -110)
Luego se divide la mantisa:
(1+1/4+1/128)2^5
2^5
fue el movimiento del lugar decimal cuando el flotador se pasó a binario. El resultado pierde algunos dígitos al convertirlo, pero se aproxima. 1.5ex10-110
Puede que haya cometido errores siguiendo a otros, pero esta es la idea general de cómo se guardan los flotadores en la memoria.