¿Cómo dividir un número de punto flotante en dígitos individuales?

Estoy ayudando a un amigo con un pequeño proyecto de electrónica utilizando un microcontrolador PIC 16F877 (A) que estoy programando con mikroC.

Me encontré con un problema que es que tengo un número de punto flotante en una variable, digamos, por ejemplo, 1234.123456 y necesito dividirlo en variables que contienen cada número individual, así que obtengo Char1 = 1, Char2 = 2, etc. para mostrar en LCD. El número siempre se redondeará a 3 o 4 decimales, por lo que debería ser necesario rastrear la ubicación del punto decimal.

Cualquier consejo sobre cómo obtener esta división sería muy apreciado.

Respuestas (3)

Hay numerosas formas de hacerlo. Puede encontrar que su compilador tiene una función de biblioteca para hacerlo por usted. Puede ser posible con:

  • sprintf() / snprintf()
  • dtostrf()
  • dtoa()

Alternativamente, no es muy difícil escribir tu propia rutina para hacerlo. Es solo un caso de primero calcular cuántos dígitos hay antes del punto decimal, dividirlo por 10 esa cantidad de veces, luego tomar la parte entera repetidamente mientras se multiplica por 10, asegurándose de agregar el punto decimal en el lugar correcto.

Entonces, en pseudocódigo, puede verse algo como:

If the value < 0.0:
    Insert - into string
    Subtract value from 0.0 to make it positive.
While the value <= 10.0:
    Divide by 10
    Increment decimal counter
For each digit of required precision:
    Take the integer portion of the value and place it in the string
    Subtract the integer portion from the value
    Decrement decimal counter
    If decimal counter is 0:
        Insert decimal point
    Multiply the value by 10.

Usa sprintf(). Funciona igual que printf(), pero "imprime" en una cadena (también conocida como matriz de caracteres).

hacer: char stringvar[10]; sprintf(varcadena, "%9.4f", varflotante);

No todas las bibliotecas tienen la parte de coma flotante de vsprintf compilada para ahorrar espacio. Especialmente en compiladores de 8 bits.
@Majenko: Eso puede ser cierto, pero si está manejando números de coma flotante, es muy probable que ya esté usando algunas bibliotecas de coma flotante.
Las bibliotecas de matemáticas de coma flotante están completamente separadas del manejo de coma flotante de printf. Por ejemplo, eche un vistazo a avr-libc de Arduino. Tiene soporte completo de coma flotante, pero no puede imprimir flotantes porque ese código se ha excluido específicamente porque es increíblemente grande.
Si está usando las bibliotecas de punto flotante, probablemente tenga mucho espacio de código y también puede incluir la biblioteca printf.
@Austin No se trata de "incluir" la "biblioteca printf", sino de si la biblioteca C incorporada tiene soporte de punto flotante en la función vsprintf. No hay nada que pueda "incluir" para cambiar eso, aparte de usar una biblioteca C diferente.
No me refiero literalmente a #incluirlo, sino a vincularlo, y por "biblioteca printf" me refiero al que tiene vsprintf, pero printf es el uso más común. Nunca he visto un vsprintf que no pueda hacer números de coma flotante.

En una MCU pequeña sin soporte de punto flotante de hardware, deberíamos hacer la menor cantidad posible de matemáticas de punto flotante y, a menos que realmente necesite la familia de funciones printf, intente evitarlo porque infla y ralentiza mucho el código.

Sugiero convertir el flotante en un entero multiplicando primero el flotante por 1,000.0 (asumiendo que desea tres lugares decimales) y luego convertirlo en un entero largo, redondeando según corresponda. Si va a mostrar el resultado en una pantalla LCD de matriz de puntos o de 7 segmentos, creo que este formato es ideal.

Supongamos que el flotante puede estar en el rango de 0 a 999.999 (negar si es negativo, guardar el signo para mostrarlo más tarde). El int largo correspondiente entonces tiene el rango de 0 a 999999. Convertiremos el número comenzando con el dígito más significativo.

Pseudocódigo:

dig6 = -1  // Init MSDigit
while number >= 0
   number = number - 100,000
   dig6 = dig6 + 1
number = number + 100,000  // Restore number, Dig 6 is done

dig5 = -1
while number >= 0
   number = number - 10,000
   dig5 = dig5 + 1
number = number + 10,000 // number can be switched to 16-bit here for speed

... dig4 and 3 in similar fashion

dig2 = -1
while number >= 0
   number = number - 10
   dig2 = dig2 + 1
dig1 = number + 10

En este punto, tiene los seis dígitos almacenados en un byte cada uno y el signo menos guardado. Si está utilizando una pantalla LCD de 7 segmentos, pase los dígitos a una función de codificador de 7 segmentos antes de escribir en la pantalla LCD. Si está utilizando una pantalla de matriz de puntos con interfaz serial, agregue 0x30 a cada dígito para la codificación ASCII. También necesitamos recordar el punto decimal entre dig4 y dig3.

Este algoritmo es bastante rápido ya que no hay multiplicación ni división involucradas. Lo he usado en pequeñas MCU de 4 bits con buenos resultados.