Je m'efforce de comparer deux nombres en virgule flottante dans un script bash. J'ai des variables, par exemple.
let num1=3.17648e-22
let num2=1.5
Maintenant, je veux juste faire une simple comparaison de ces deux nombres:
st=`echo "$num1 < $num2" | bc`
if [ $st -eq 1]; then
echo -e "$num1 < $num2"
else
echo -e "$num1 >= $num2"
fi
Malheureusement, le traitement correct du num1, qui peut être du "format électronique", me pose quelques problèmes. :(
Toute aide, des conseils sont les bienvenus!
Cela peut être fait plus facilement en utilisant le contexte numérique de Bash:
if (( $(echo "$num1 > $num2" |bc -l) )); then
…
fi
La canalisation à travers la commande de base de la calculatrice bc
renvoie 1 ou 0.
L'option -l
est équivalente à --mathlib
; il charge la bibliothèque mathématique standard.
Le fait de placer l'expression entière entre doubles parenthèses (( ))
traduira ces valeurs en respectivement vrai ou faux.
Assurez-vous que le package bc
basic calculator est installé.
bash ne traite que les mathématiques entières mais vous pouvez utiliser la commande bc
comme suit:
$ num1=3.17648E-22
$ num2=1.5
$ echo $num1'>'$num2 | bc -l
0
$ echo $num2'>'$num1 | bc -l
1
Notez que le signe de l'exposant doit être en majuscule
Il vaut mieux utiliser awk
pour les mathématiques non entières. Vous pouvez utiliser cette fonction utilitaire bash:
numCompare() {
awk -v n1="$1" -v n2="$2" 'BEGIN {printf "%s " (n1<n2?"<":">=") " %s\n", n1, n2}'
}
Et appelez ça comme:
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
Solution pure bash pour comparer des flottants sans notation exponentielle, zéros au début ou à la fin:
if [ ${FOO%.*} -eq ${BAR%.*} ] && [ ${FOO#*.} \> ${BAR#*.} ] || [ ${FOO%.*} -gt ${BAR%.*} ]; then
echo "${FOO} > ${BAR}";
else
echo "${FOO} <= ${BAR}";
fi
Ordre des opérateurs logiques importe . Les parties entières sont comparées en tant que nombres et les fractions sont volontairement comparées en tant que chaînes. Les variables sont scindées en parties entières et fractionnaires à l'aide de cette méthode .
Ne comparera pas les flottants aux entiers (sans point).
vous pouvez utiliser awk associé à bash si condition, awk affichera 1 ou 0 et ceux-ci seront interprétés par la clause if avec true ou false .
if (( $(awk 'BEGIN {print ("'$d1'" >= "'$d2'")}') )); then
echo "yes"
else
echo "no"
fi
méfiez-vous lorsque vous comparez des numéros de version de package, par exemple en vérifiant si grep 2.20 est supérieur à la version 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
J'ai résolu ce problème avec une telle fonction 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
J'ai utilisé les réponses d'ici et les ai mises dans une fonction, vous pouvez l'utiliser comme ceci:
is_first_floating_number_bigger 1.5 1.2
result="${__FUNCTION_RETURN}"
Une fois appelé, echo $result
sera 1
dans ce cas, sinon 0
.
La fonction:
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}"
}
Ou une version avec sortie de débogage:
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}"
}
Enregistrez simplement la fonction dans un fichier .sh
séparé et incluez-la comme ceci:
. /path/to/the/new-file.sh
Ce script peut aider lorsque je vérifie si la version grails
installée est supérieure au minimum requis. J'espère que ça aide.
#!/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
Bien sûr, si vous n’avez pas besoin d’arithmétique à virgule flottante, arithmétique sur, par exemple. Les valeurs en dollars où il y a toujours exactement deux chiffres décimaux, vous pouvez simplement supprimer le point (en multipliant effectivement par 100) et comparer les entiers résultants.
if [[ ${num1/.} < ${num2/.} ]]; then
...
Cela nécessite évidemment que vous soyez sûr que les deux valeurs ont le même nombre de décimales.
veuillez vérifier le code édité ci-dessous: -
#!/bin/bash
export num1=(3.17648*e-22)
export num2=1.5
st=$((`echo "$num1 < $num2"| bc`))
if [ $st -eq 1 ]
then
echo -e "$num1 < $num2"
else
echo -e "$num1 >= $num2"
fi
ça marche bien.
Utilisez korn Shell, vous devrez peut-être comparer séparément la partie décimale
#!/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
En utilisant bashj ( https://sourceforge.net/projects/bashj/ ), un mutant bash supportant Java, il vous suffit d’écrire (et cela IS facile à lire):
#!/usr/bin/bashj
#!Java
static int doubleCompare(double a,double b) {return((a>b) ? 1 : (a<b) ? -1 : 0);}
#!bashj
num1=3.17648e-22
num2=1.5
comp=j.doubleCompare($num1,$num2)
if [ $comp == 0 ] ; then echo "Equal" ; fi
if [ $comp == 1 ] ; then echo "$num1 > $num2" ; fi
if [ $comp == -1 ] ; then echo "$num2 > $num1" ; fi
Bien sûr, l'hybridation bashj bash/Java offre beaucoup plus ...
Une solution prenant en charge la notation scientifique avec les exposants majuscules et minuscules (par exemple, 12.00e4
):
if (( $(bc -l <<< "${value1/e/E} < ${value2/e/E}") ))
then
echo "$value1 is less than $value2"
fi
Que dis-tu de ça? = 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
num1=0.555
num2=2.555
if [ `echo "$num1>$num2"|bc` -eq 1 ]; then
echo "$num1 is greater then $num2"
else
echo "$num2 is greater then $num1"
fi
Je publiais ceci comme une réponse à https://stackoverflow.com/a/56415379/1745001 quand il a été fermé comme un dup de cette question, donc voici ce qui est appliqué ici:
Pour des raisons de simplicité et de clarté, utilisez simplement awk pour les calculs car il s’agit d’un outil UNIX standard et donc tout aussi probable d’être présent que bc et beaucoup plus facile à utiliser syntaxiquement.
Pour cette question:
$ cat tst.sh
#!/bin/bash
num1=3.17648e-22
num2=1.5
awk -v num1="$num1" -v num2="$num2" '
BEGIN {
print "num1", (num1 < num2 ? "<" : ">="), "num2"
}
'
$ ./tst.sh
num1 < num2
et pour cette autre question qui était fermée comme un dup de celle-ci:
$ cat tst.sh
#!/bin/bash
read -p "Operator: " operator
read -p "First number: " ch1
read -p "Second number: " ch2
awk -v ch1="$ch1" -v ch2="$ch2" -v op="$operator" '
BEGIN {
if ( ( op == "/" ) && ( ch2 == 0 ) ) {
print "Nope..."
}
else {
print ch1 '"$operator"' ch2
}
}
'
$ ./tst.sh
Operator: /
First number: 4.5
Second number: 2
2.25
$ ./tst.sh
Operator: /
First number: 4.5
Second number: 0
Nope...
awk
et des outils comme celui-ci (je vous regarde sed
...) devraient être relégués à la poubelle d'anciens projets, avec un code que tout le monde a trop peur de toucher car il a été écrit dans un langage illimité.
Ou vous êtes le projet relativement rare qui doit prioriser l'optimisation de l'utilisation du processeur par rapport à l'optimisation de la maintenance du code ... dans ce cas, poursuivez.
Sinon, pourquoi ne pas utiliser simplement quelque chose de lisible et explicite, tel que python
? Vos collègues codeurs et futurs collaborateurs vous remercieront. Vous pouvez utiliser python
inline avec bash comme tous les autres.
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