it-swarm.com.de

Wie vergleicht man zwei Gleitkommazahlen in Bash?

Ich bemühe mich, zwei Gleitkommazahlen in einem Bash-Skript zu vergleichen. Ich muss Variablen haben, z.

let num1=3.17648e-22
let num2=1.5

Nun möchte ich nur einen einfachen Vergleich dieser beiden Zahlen durchführen:

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

Leider habe ich einige Probleme mit der richtigen Behandlung der Nummer 1, die vom "E-Format" sein kann. :(

Hilfe, Hinweise sind willkommen!

97
Jonas

Bequemer

Dies kann bequemer mit dem numerischen Kontext von Bash durchgeführt werden:

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

Erläuterung

Wenn Sie den grundlegenden Taschenrechnerbefehl bc verwenden, wird entweder 1 oder 0 zurückgegeben.

Die Option -l entspricht --mathlib; Es lädt die Standard-Mathematikbibliothek.

Durch Einschließen des gesamten Ausdrucks in doppelte Klammern (( )) werden diese Werte in wahr bzw. falsch übersetzt.

Bitte stellen Sie sicher, dass das Basisrechnerpaket bc installiert ist.

102

bash behandelt nur Ganzzahlen maths Sie können den Befehl bc jedoch wie folgt verwenden:

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

Beachten Sie, dass das Exponentenzeichen Großbuchstaben sein muss

94
alrusdi

Es ist besser, awk für nicht ganzzahlige Mathematik zu verwenden. Sie können diese Bash-Utility-Funktion verwenden:

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

Und nennen Sie es als:

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
23
anubhava

Pure Bash-Lösung zum Vergleich von Floats ohne Exponentialschreibweise, führende oder nachgestellte Nullen

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

Reihenfolge der logischen Operatoren matters . Ganzzahlige Teile werden als Zahlen verglichen und Bruchteile werden absichtlich als Zeichenfolgen verglichen. Variablen werden mit dieser Methode in ganzzahlige und gebrochene Teile aufgeteilt.

Kann Floats nicht mit ganzen Zahlen vergleichen (ohne Punkt).

20
user

sie können awk in Kombination mit einer bash verwenden, wenn bedingung, awk druckt 1 oder 0 und diese werden von if-Klauseln mit true oder false interpretiert.

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

vorsicht beim Vergleich von Zahlen, die Paketversionen sind, zum Beispiel, ob grep 2.20 größer als Version 2.6 ist:

$ 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

Ich habe ein Problem mit dieser Shell/awk-Funktion gelöst:

# 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
5
Elan Ruusamäe

Ich habe die Antworten von hier verwendet und sie in eine Funktion eingefügt. Sie können sie so verwenden:

is_first_floating_number_bigger 1.5 1.2
result="${__FUNCTION_RETURN}"

Nach dem Aufruf wird echo $result in diesem Fall 1 sein, andernfalls 0.

Die Funktion:

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

Oder eine Version mit Debug-Ausgabe:

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

Speichern Sie einfach die Funktion in einer separaten .sh-Datei und fügen Sie sie wie folgt ein:

. /path/to/the/new-file.sh
3
Thomas Kekeisen

Dieses Skript kann hilfreich sein, wenn überprüft wird, ob die grails-Version größer als die erforderliche Mindestversion ist. Ich hoffe es hilft. 

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

Wenn Sie wirklich keine Fließkomma-Arithmetik benötigen, müssen Sie nur die Arithmetik von z. Dollar-Werte, bei denen es immer genau zwei Dezimalstellen gibt, können Sie den Punkt einfach löschen (effektiv mit 100 multiplizieren) und die resultierenden ganzen Zahlen vergleichen.

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

Dies erfordert natürlich, dass Sie sicher sind, dass beide Werte die gleiche Anzahl von Dezimalstellen haben.

1
tripleee

bitte überprüfen Sie den unten bearbeiteten Code: -

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

das funktioniert gut.

1
gopika

Verwenden Sie Korn-Shell, in Bash müssen Sie möglicherweise den Dezimalteil separat vergleichen

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

Mit bashj ( https://sourceforge.net/projects/bashj/ ), einem bash-Mutanten mit Java-Unterstützung, schreiben Sie einfach (und es IS einfach zu lesen)

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

Natürlich bietet bashj bash/Java Hybridation noch viel mehr ...

1
Fil

Eine Lösung, die die wissenschaftliche Notation mit Exponenten in Groß- und Kleinschreibung unterstützt (z. B. 12.00e4):

if (( $(bc -l <<< "${value1/e/E} < ${value2/e/E}") ))
then
    echo "$value1 is less than $value2"
fi 
0
Danila Piatov

Wie wäre es damit? = 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
0
Eduardo Lucio
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
rmil

Ich habe dies als Antwort auf https://stackoverflow.com/a/56415379/1745001 gepostet, als es wegen dieser Frage geschlossen wurde.

Der Einfachheit und Klarheit halber verwenden Sie für die Berechnungen einfach awk, da es sich um ein Standard-UNIX-Tool handelt und daher wahrscheinlich genauso vorhanden ist wie bc und das syntaktische Arbeiten viel einfacher ist.

Für diese Frage:

$ 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

und für diese andere Frage, die als Dup von dieser geschlossen wurde:

$ 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...
0
Ed Morton

awk und ähnliche Werkzeuge (ich starre dich an sed...) sollten in den Mülleimer alter Projekte verbannt werden, mit Code, den jeder zu viel Angst hat, da er in einer Read-Never-Sprache geschrieben wurde.

Oder Sie sind das relativ seltene Projekt, das der Optimierung der CPU-Nutzung Priorität gegenüber der Optimierung der Codewartung einräumen muss. In diesem Fall machen Sie weiter.

Wenn nicht, warum dann nicht einfach etwas lesbares und explizites wie python verwenden? Ihre Kollegen und Ihr zukünftiges Selbst werden es Ihnen danken. Sie können python inline wie alle anderen in Bash verwenden.

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
CivFan