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!

85
demandé sur codeforester 2011-12-28 13:27:45

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

90
répondu alrusdi 2016-07-01 21:47:29

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é.

84
répondu Serge Stroobandt 2018-09-06 18:51:15

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
22
répondu anubhava 2017-07-19 15:52:52

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).

19
répondu user 2017-05-23 12:34:54

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
8
répondu ungalcrys 2017-08-10 09:11:56

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
4
répondu Elan Ruusamäe 2015-04-29 15:41:36

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
3
répondu Thomas Kekeisen 2016-08-07 08:25:11

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
2
répondu prayagupd 2018-05-10 20:01:24

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
1
répondu Alan Joseph 2013-09-14 04:17:50

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.

1
répondu tripleee 2017-12-08 10:49:29

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...

1
répondu Fil 2018-06-19 18:55:27

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
0
répondu Eduardo Lucio 2017-05-29 21:59:30

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
0
répondu CivFan 2017-09-25 16:28:40
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
0
répondu rmil 2018-09-23 22:14:20