Comment comparer deux nombres à virgule flottante à Bash?
j'essaie de comparer deux nombres à virgule flottante dans un script bash. J'ai de variables, par exemple
let num1=3.17648e-22
let num2=1.5
maintenant, je veux juste faire une comparaison simple 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, j'ai quelques problèmes avec le bon traitement du num1 qui peut être du "E-format". : (
toute aide, conseils sont les bienvenus!
14 réponses
bash gère entiers des mathématiques
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
noter que le signe exposant doit être en majuscule
plus commodément
cela peut être fait plus commodément en utilisant le contexte numérique de Bash:
if (( $(echo "$num1 > $num2" |bc -l) )); then
…
fi
explication
de la Tuyauterie grâce à la calculatrice de base de la commande bc
retourne 1 ou 0. En plaçant l'expression entière entre les doubles parenthèses (( ))
, ces valeurs se traduiront respectivement par true ou false.
veuillez vous assurer que la calculatrice de base bc
paquet est installé.
il est préférable d'utiliser awk
pour les mathématiques non entières. Vous pouvez utiliser cette fonction utilitaire bash:
numCompare() {
awk -v n1="" -v n2="" 'BEGIN {printf "%s " (n1<n2?"<":">=") " %s\n", n1, n2}'
}
et appelez-le 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
Pur bash solution pour comparer les chars sans notation exponentielle, chef de file ou les zéros à droite:
if [ ${FOO%.*} -eq ${BAR%.*} ] && [ ${FOO#*.} \> ${BAR#*.} ] || [ ${FOO%.*} -gt ${BAR%.*} ]; then
echo "${FOO} > ${BAR}";
else
echo "${FOO} <= ${BAR}";
fi
Afin d'opérateurs logiques questions . Les parties entières sont comparées en tant que nombres et les parties fractionnaires sont intentionnellement comparées en tant que chaînes. Les Variables sont divisées en parties entières et fractionnelles en utilisant cette méthode .
ne comparera pas les flotteurs avec les entiers (sans point).
vous pouvez utiliser awk combiné avec un bash si condition, awk va imprimer 1 ou 0 et ceux seront interprétés par si clause avec vrai ou faux .
if (( $(awk 'BEGIN {print ("'$d1'" >= "'$d2'")}') )); then
echo "yes"
else
echo "no"
fi
attention lorsque vous comparez des nombres qui sont des versions de paquets, comme vérifier 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 un tel problème avec une telle fonction shell / awk:
# get version of GNU tool
toolversion() {
local prog="" operator="" value="" 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 mettre 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=""
number2=""
[ ${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=""
number2=""
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}"
}
il suffit de sauvegarder la fonction dans un fichier séparé .sh
et de l'inclure comme ceci:
. /path/to/the/new-file.sh
ce script peut être utile lorsque je vérifie si la version grails
installée est supérieure au minimum requis. Espérons que cela 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
utilisez korn shell, dans bash vous pouvez avoir à comparer la partie décimale séparément
#!/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
bien sûr, si vous n'avez pas besoin vraiment arithmétique flottante, juste arithmétique sur par exemple les valeurs en dollars où il y a toujours exactement deux décimales, vous pourriez juste laisser tomber le point (se multipliant effectivement par 100) et comparer les entiers qui en résultent.
if [[ ${num1/.} < ${num2/.} ]]; then
...
cela exige évidemment que vous soyez sûr que les deux valeurs ont le même nombre de décimales.
utilisant bashj ( https://sourceforge.net/projects/bashj / ), un mutant bash avec support java, il suffit d'écrire (et il est 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, bashj bash/java hybridation offre bien plus...
et ça? = D
VAL_TO_CHECK="1.00001"
if [ $(awk '{printf( >= ) ? 1 : 0}' <<<" $VAL_TO_CHECK 1 ") -eq 1 ] ; then
echo "$VAL_TO_CHECK >= 1"
else
echo "$VAL_TO_CHECK < 1"
fi
awk
et des outils comme celui-ci (je vous regarde sed
...) devrait être relégué à la poubelle des vieux projets, avec un code que tout le monde a trop peur de toucher puisqu'il a été écrit dans un langage lu-jamais.
ou vous êtes le projet relativement rare qui a besoin de prioriser l'optimisation de L'utilisation CPU sur l'optimisation de la maintenance du code... dans ce cas, porter sur.
si ce n'est pas le cas, pourquoi ne pas plutôt utiliser quelque chose de lisible et explicite, comme python
? Vos collègues codeurs et futur 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
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