Come confrontare due numeri in virgola mobile in Bash?

Sto provando a confrontare due numeri in virgola mobile all’interno di uno script bash. Devo variabili, ad es

let num1=3.17648e-22 let num2=1.5 

Ora, voglio solo fare un semplice confronto di questi due numeri:

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

Sfortunatamente, ho alcuni problemi con il giusto trattamento del num1 che può essere del “formato elettronico”. 🙁

Qualsiasi aiuto, suggerimenti sono ben accetti!

bash gestisce solo i numeri interi ma puoi usare il comando bc come segue:

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

Nota che il segno dell’esponente deve essere maiuscolo

Più convenientemente

Questo può essere fatto più convenientemente usando il contesto numerico di Bash:

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

Spiegazione:

bc restituisce 1 o 0 e racchiudendo l’intera espressione tra doppia parentesi (( )) tradurrà questi valori rispettivamente in true o false.

Si prega di assicurarsi che il pacchetto del calcolatore di bc sia installato.

È meglio usare awk per matematica non intera. È ansible utilizzare questa funzione di utilità bash:

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

E chiamalo come:

 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 

Soluzione bash pura per confrontare i float senza notazione esponenziale, zeri iniziali o finali:

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

L'ordine degli operatori logici è importante . Le parti intere vengono confrontate come numeri e le parti frazionarie vengono confrontate intenzionalmente come stringhe. Le variabili sono suddivise in parti intere e frazionarie utilizzando questo metodo .

Non confronterà i float con numeri interi (senza punto).

puoi usare awk combinato con una condizione bash if, awk stamperà 1 o 0 e quelli saranno interpretati da clausola if con true o false .

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

fai attenzione quando confronti i numeri che sono versioni del pacchetto, come verificare se grep 2.20 è maggiore della versione 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 

Ho risolto questo problema con tale funzione 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 

Ho usato le risposte da qui e le ho inserite in una funzione, puoi usarla in questo modo:

 is_first_floating_number_bigger 1.5 1.2 result="${__FUNCTION_RETURN}" 

Una volta chiamato, echo $result sarà 1 in questo caso, altrimenti 0 .

La funzione:

 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 versione con output di debug:

 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}" } 

Basta salvare la funzione in un file .sh separato e includerla in questo modo:

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

Questo script può aiutare dove sto controllando se la versione di grails installata è maggiore del minimo richiesto. Spero che sia d’aiuto.

 #!/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 

Usa la shell korn, in bash potresti dover confrontare la parte decimale separatamente

 #!/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 

Naturalmente, se non hai bisogno di aritmetica a virgola mobile, solo aritmetica su valori in dollari, dove ci sono sempre esattamente due cifre decimali, potresti semplicemente eliminare il punto (moltiplicando effettivamente per 100) e confrontare gli interi risultanti.

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

Questo ovviamente richiede che tu sia sicuro che entrambi i valori abbiano lo stesso numero di cifre decimali.

Usando bashj ( https://sourceforge.net/projects/bashj/ ), un mutante bash con supporto java, basta scrivere (ed è facile da leggere):

 #!/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 

Ovviamente bashj bash / java hybridation offre molto di più …

Cosa ne pensi di questo? = 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 e strumenti come questo (sto fissando te …) dovrebbero essere relegati nella pattumiera di vecchi progetti, con il codice che tutti hanno troppa paura di toccare poiché è stato scritto in un linguaggio mai letto.

Oppure sei il progetto relativamente raro che deve dare priorità all’ottimizzazione dell’utilizzo della CPU rispetto all’ottimizzazione della manutenzione del codice … nel qual caso, continua.

Se no, però, perché non usare invece qualcosa di leggibile ed esplicito, come python ? I tuoi colleghi programmatori e te stesso futuro ti ringrazieranno. Puoi usare python linea con bash proprio come tutti gli altri.

 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