¿Cómo comparar dos números de coma flotante en Bash?

Estoy tratando de comparar dos números de punto flotante dentro de un script bash. Tengo que variables, por ejemplo

let num1=3.17648e-22 let num2=1.5 

Ahora, solo quiero hacer una comparación simple de estos dos números:

 st=`echo "$num1 < $num2" | bc` if [ $st -eq 1]; then echo -e "$num1 = $num2" fi 

Desafortunadamente, tengo algunos problemas con el tratamiento correcto del número 1 que puede ser del “formato electrónico”. 🙁

Cualquier ayuda, sugerencias son bienvenidas!

bash solo maneja números enteros, pero puede usar el comando bc siguiente manera:

 $ num1=3.17648E-22 $ num2=1.5 $ echo $num1'>'$num2 | bc -l 0 $ echo $num2'>'$num1 | bc -l 1 

Tenga en cuenta que el signo del exponente debe ser mayúscula

Más convenientemente

Esto se puede hacer de manera más conveniente usando el contexto numérico de Bash:

 if (( $(echo "$num1 > $num2" | bc -l) )); then … fi 

Explicación:

bc devuelve 1 o 0 y al encerrar toda la expresión entre doble paréntesis (( )) se traducirán estos valores a respectivamente verdadero o falso.

Por favor, asegúrese de que el paquete de calculadora bc esté instalado.

Es mejor usar awk para las matemáticas no enteras. Puedes usar esta función de utilidad bash:

 numCompare() { awk -v n1="$1" -v n2="$2" 'BEGIN {printf "%s " (n1=") " %s\n", n1, n2}' } 

Y llámalo como:

 numCompare 5.65 3.14e-22 5.65 >= 3.14e-22 numCompare 5.65e-23 3.14e-22 5.65e-23 < 3.14e-22 numCompare 3.145678 3.145679 3.145678 < 3.145679 

Solución de bash pura para comparar flotantes sin notación exponencial, ceros iniciales o finales:

 if [ ${FOO%.*} -eq ${BAR%.*} ] && [ ${FOO#*.} \> ${BAR#*.} ] || [ ${FOO%.*} -gt ${BAR%.*} ]; then echo "${FOO} > ${BAR}"; else echo "${FOO} <= ${BAR}"; fi 

El orden de los operadores lógicos importa . Las partes enteras se comparan cuando los números y las partes fraccionales se comparan intencionalmente como cadenas. Las variables se dividen en partes enteras y fraccionarias usando este método .

No se compararán los flotantes con números enteros (sin punto).

puede usar awk combinado con una condición bash if, awk imprimirá 1 o 0 y esos serán interpretados por cláusula if con verdadero o falso .

 if (( $(awk 'BEGIN {print ("'$d1'" >= "'$d2'")}') )); then echo "yes" else echo "no" fi 

tenga cuidado al comparar números que son versiones de paquete, como comprobar si grep 2.20 es mayor que la versión 2.6:

 $ awk 'BEGIN { print (2.20 >= 2.6) ? "YES" : "NO" }' NO $ awk 'BEGIN { print (2.2 >= 2.6) ? "YES" : "NO" }' NO $ awk 'BEGIN { print (2.60 == 2.6) ? "YES" : "NO" }' YES 

Resolví tal problema con tal función shell / awk:

 # get version of GNU tool toolversion() { local prog="$1" operator="$2" value="$3" version version=$($prog --version | awk '{print $NF; exit}') awk -vv1="$version" -vv2="$value" 'BEGIN { split(v1, a, /\./); split(v2, b, /\./); if (a[1] == b[1]) { exit (a[2] '$operator' b[2]) ? 0 : 1 } else { exit (a[1] '$operator' b[1]) ? 0 : 1 } }' } if toolversion grep '>=' 2.6; then # do something awesome fi 

Utilicé las respuestas de aquí y las puse en una función, puedes usarla así:

 is_first_floating_number_bigger 1.5 1.2 result="${__FUNCTION_RETURN}" 

Una vez llamado, echo $result será 1 en este caso, de lo contrario 0 .

La función:

 is_first_floating_number_bigger () { number1="$1" number2="$2" [ ${number1%.*} -eq ${number2%.*} ] && [ ${number1#*.} \> ${number2#*.} ] || [ ${number1%.*} -gt ${number2%.*} ]; result=$? if [ "$result" -eq 0 ]; then result=1; else result=0; fi __FUNCTION_RETURN="${result}" } 

O una versión con salida de depuración:

 is_first_floating_number_bigger () { number1="$1" number2="$2" echo "... is_first_floating_number_bigger: comparing ${number1} with ${number2} (to check if the first one is bigger)" [ ${number1%.*} -eq ${number2%.*} ] && [ ${number1#*.} \> ${number2#*.} ] || [ ${number1%.*} -gt ${number2%.*} ]; result=$? if [ "$result" -eq 0 ]; then result=1; else result=0; fi echo "... is_first_floating_number_bigger: result is: ${result}" if [ "$result" -eq 0 ]; then echo "... is_first_floating_number_bigger: ${number1} is not bigger than ${number2}" else echo "... is_first_floating_number_bigger: ${number1} is bigger than ${number2}" fi __FUNCTION_RETURN="${result}" } 

Simplemente guarde la función en un archivo .sh separado e .sh así:

 . /path/to/the/new-file.sh 

Esta secuencia de comandos puede ayudar a verificar si la versión de grails instalada es mayor que el mínimo requerido. Espero eso ayude.

 #!/bin/bash min=1.4 current=`echo $(grails --version | head -n 2 | awk '{print $NF}' | cut -c 1-3)` if [ 1 -eq `echo "${current} < ${min}" | bc` ] then echo "yo, you have older version of grails." else echo "Hurray, you have the latest version" fi 

Use korn shell, en bash puede que tenga que comparar la parte decimal por separado

 #!/bin/ksh X=0.2 Y=0.2 echo $X echo $Y if [[ $X -lt $Y ]] then echo "X is less than Y" elif [[ $X -gt $Y ]] then echo "X is greater than Y" elif [[ $X -eq $Y ]] then echo "X is equal to Y" fi 

Por supuesto, si no necesita realmente una aritmética de coma flotante, solo aritmética en, por ejemplo, valores en dólares, donde siempre hay exactamente dos dígitos decimales, puede simplemente colocar el punto (multiplicando efectivamente por 100) y comparar los enteros resultantes.

 if [[ ${num1/.} < ${num2/.} ]]; then ... 

Obviamente, esto requiere que esté seguro de que ambos valores tienen la misma cantidad de decimales.

Usando bashj ( https://sourceforge.net/projects/bashj/ ), un mutante bash con soporte java, solo escribes (y ES fácil de leer):

 #!/usr/bin/bashj #!java static int doubleCompare(double a,double b) {return((a>b) ? 1 : (a $num2" ; fi if [ $comp == -1 ] ; then echo "$num2 > $num1" ; fi 

Por supuesto bashj bash / java hybridation ofrece mucho más …

¿Qué tal esto? = D

 VAL_TO_CHECK="1.00001" if [ $(awk '{printf($1 >= $2) ? 1 : 0}' <<<" $VAL_TO_CHECK 1 ") -eq 1 ] ; then echo "$VAL_TO_CHECK >= 1" else echo "$VAL_TO_CHECK < 1" fi 

awk y herramientas como esta (estoy mirando a ti sed …) deberían ser relegadas al basurero de proyectos antiguos, con un código que todos temen tocar ya que está escrito en un lenguaje de “nunca lo hables”.

O bien, es el proyecto relativamente raro que necesita priorizar la optimización del uso de la CPU sobre la optimización del mantenimiento del código … en cuyo caso, continúe.

Sin embargo, si no es así, ¿por qué no usar algo legible y explícito, como python ? Sus codificadores compañeros y yo futuro se lo agradecerán. Puedes usar python línea con bash como todos los demás.

 num1=3.17648E-22 num2=1.5 if python -c "import sys; sys.exit(0 if float($num1) < float($num2) else 1)"; then echo "yes, $num1 < $num2" else echo "no, $num1 >= $num2" fi