Cómo realizar cálculos de punto flotante en scripts Bash de Linux

Puntos clave
- El shell Linux Bash solo admite aritmética de enteros y no puede realizar cálculos con números de punto flotante.
- La utilidad bc en Linux permite realizar cálculos precisos de punto flotante de forma interactiva y en scripts de shell.
- Con bc, puede establecer el número de decimales para mostrar y realizar cálculos con precisión arbitraria, incluido el uso de funciones de biblioteca matemática estándar.
El shell de Linux Bash solo admite aritmética de enteros. No puede comprender ni manejar cálculos de punto flotante. La utilidad bc proporciona cálculos precisos de punto flotante de forma interactiva y en scripts de shell.
Porque Bash solo admite números enteros
La decisión de diseño original de limitar el shell Bourne de Unix a la aritmética de enteros puede haber tenido su origen en la asignación inicial de un número entero en un solo byte de RAM. Quizás nunca sepamos qué hubo realmente detrás de esa decisión. Tampoco, de hecho, por qué la versión Linux del shell Bourne, el shell Bash, decidió hacer lo mismo.
Por sí solo, Bash no puede realizar cálculos con números de punto flotante, y los cálculos con números enteros que tendrían una parte fraccionaria en la respuesta se informan como valores enteros truncados. Esto es cierto en la línea de comando y en los scripts de shell Bash. Dependiendo del caso de uso, esto puede resultar problemático o perturbador.
Linux viene con dos aplicaciones de utilidad que le permiten realizar cálculos de punto flotante. Uno de ellos es DC. Es un poco divertido porque funciona en notación polaca inversa. La otra herramienta es bc. Se puede utilizar de forma interactiva o como comando y es la solución de la que hablaremos aquí.
Relacionado: 9 ejemplos de secuencias de comandos Bash
El problema
Dejemos que Bash divida seis por tres.
echo $((6 / 3))
Obtenemos nuestra respuesta esperada de dos. Ahora dividamos seis por siete. Claramente, esto tendrá una respuesta fraccionaria.
echo $((6 / 7))
Obviamente Zero está equivocado. Intentemos de nuevo, dividiendo 16 entre 7.
echo $((16 / 7))
Obtenemos una respuesta de dos. Lo que sucede es que se descarta la parte fraccionaria de la respuesta, por lo que se trunca la respuesta. En el primer ejemplo no había parte fraccionaria, por lo que obtenemos la respuesta correcta.
El segundo ejemplo no tenía elementos enteros en la respuesta, solo una parte fraccionaria. Como se ha descartado la parte fraccionaria, la respuesta que se nos muestra es cero.
En el tercer ejemplo, 7 se divide dos veces entre 16, con un resto fraccionario. Nuevamente se descarta el resto y se trunca el resultado.
Usando bc interactivamente
Puede utilizar bc como calculadora interactiva escribiendo bc y presionando la tecla "Enter".
bc
La aplicación bc se inicia, anuncia el número de versión y luego espera su entrada. Al escribir un cálculo y presionar "Entrar", se evaluará el cálculo y se mostrará la respuesta.
16 * 41024 / 32
2^2 * 1024
Puede utilizar "Ctrl+L" para borrar la pantalla y "Ctrl+D" para salir del programa. Probemos un cálculo que tendrá un componente fraccionario en la respuesta.
22 / 7
No es lo que esperábamos. Contrariamente a la intuición, aunque bc nos permite usar precisión arbitraria, de forma predeterminada no mostrará el punto decimal ni los dígitos que le siguen.
Para hacer visible la respuesta verdadera, necesitamos decirle a bc cuántos decimales mostrar. Esto lo hacemos con el comando “escalas”. Le pediremos siete decimales y reharemos el cálculo.
scale=7
22 / 7
Finalmente estamos llegando a alguna parte. La configuración de "escala" permanece vigente hasta que la cambie. Configurar el número de decimales le indica a bc el número máximo de dígitos que se mostrarán. Si una respuesta no requiere tantos decimales, se muestra con el número requerido de decimales y nada más. No está lleno de ceros sin sentido.
scale=10
0.300003 * 0.5
Puede enumerar diferentes cálculos en la misma línea usando un punto y coma ";" para separarlos. Las respuestas se muestran por fila como de costumbre, en el orden en que se enumeraron los cálculos.
25 * 6; 12.5 + 45.001; 3 + 5 + 7 + 9
También puede incluir el comando "escala" en la lista.
scale=8; 22 / 7; scale=3; 0.3 * 0.071
La biblioteca de matemáticas estándar
La opción -l (biblioteca matemática estándar) hace que bc cargue un conjunto de funciones y establezca la "escala" en 20 decimales.
bc -l
22 / 7
Con la biblioteca estándar cargada, puede utilizar estas funciones en sus cálculos.
- s(x): El pecho de x
- c(x): El coseno de x.
- hacha): El arcotangente de x
- l(x): El logaritmo natural de x
- y (x): El exponencial de e en el valor de x
- j(n,x): La función de Bessel de orden entero n de x.
El seno, el coseno y el arcotangente utilizan valores en radianes.
s (1.1)
c (.891207)
a (.628473)
Enviar entrada a bc en la línea de comando
Puede utilizar redirección y canalizaciones para enviar entradas a bc. Procese la entrada y muestre la respuesta en la ventana del terminal.
Puede redirigir a bc con o sin la opción -l (biblioteca matemática estándar).
bc <<< 22/7
bc -l <<< 22/7
Para redirigir la entrada a bc, la entrada debe ser la salida de otro proceso. Es conveniente utilizar eco para esto.
echo 22/7 | bc
echo 22/7 | bc -l
Si hay espacios en la entrada o desea incluir el comando "escala", escriba la entrada entre comillas.
echo "22 / 7" | bc -l
echo "scale=6; 22 / 7" | bc
Usando bc en scripts de shell Bash
Ahora tenemos todo lo que necesitamos para poder realizar cálculos de punto flotante en nuestros scripts bash, con la precisión que elijamos. También podemos hacer referencia a variables de Bash en nuestros cálculos, incluidos los parámetros del script.
Aquí está nuestro script de ejemplo. Copie este texto en un editor, guárdelo como "pi.sh" y luego cierre el editor.
#!/bin/bashfirst_number=22
second_number=7
pi=$(echo "scale=$1; $first_number/$second_number" | bc)
echo "Pi to $1 decimal places is: $pi"
Usamos dos variables, "primer_número" y "segundo_número" para contener dos valores numéricos. Usamos esas variables en la entrada que estamos conectando a bc.
También utilizamos el primer parámetro de línea de comando pasado al script, "$1", como valor para establecer "escala".
Antes de que podamos probar nuestro script, debemos hacerlo ejecutable con chmod.
chmod +x pi.sh
Probemos nuestro script con diferentes valores de línea de comando.
./pi.sh 5
./pi.sh 14
./pi.sh 20
Nos mostramos más en la cantidad de posiciones que especificamos en la línea de comando en nuestro script.
Relacionado: Cómo utilizar getopts para analizar las opciones del script de shell de Linux
todo suma
Ir más allá de los límites de las matemáticas de solo números enteros de Bash brinda precisión y exactitud a nuestros scripts.
Usar echo para canalizar la entrada a bc dentro de los scripts es un poco engorroso, pero funciona perfectamente y vale la pena el esfuerzo.