Ich möchte zwei Gleitkommazahlen in einem Shell-Skript vergleichen. Der folgende Code funktioniert nicht:
#!/bin/bash
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo $min
Ich möchte zwei Gleitkommazahlen in einem Shell-Skript vergleichen. Der folgende Code funktioniert nicht:
#!/bin/bash
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo $min
Antworten:
Sie können den ganzzahligen und den gebrochenen Teil getrennt prüfen:
#!/bin/bash
min=12.45
val=12.35
if (( ${val%%.*} < ${min%%.*} || ( ${val%%.*} == ${min%%.*} && ${val##*.} < ${min##*.} ) )) ; then
min=$val
fi
echo $min
Wie in den Kommentaren geäußert, funktioniert es nur, wenn beide Zahlen Bruchteile und beide Bruchteile die gleiche Anzahl von Ziffern haben. Hier ist eine Version, die für Integer- oder Fractional-Operatoren und alle Bash-Operatoren funktioniert:
#!/bin/bash
shopt -s extglob
fcomp() {
local oldIFS="$IFS" op=$2 x y digitx digity
IFS='.' x=( ${1##+([0]|[-]|[+])}) y=( ${3##+([0]|[-]|[+])}) IFS="$oldIFS"
while [[ "${x[1]}${y[1]}" =~ [^0] ]]; do
digitx=${x[1]:0:1} digity=${y[1]:0:1}
(( x[0] = x[0] * 10 + ${digitx:-0} , y[0] = y[0] * 10 + ${digity:-0} ))
x[1]=${x[1]:1} y[1]=${y[1]:1}
done
[[ ${1:0:1} == '-' ]] && (( x[0] *= -1 ))
[[ ${3:0:1} == '-' ]] && (( y[0] *= -1 ))
(( ${x:-0} $op ${y:-0} ))
}
for op in '==' '!=' '>' '<' '<=' '>='; do
fcomp $1 $op $2 && echo "$1 $op $2"
done
1.00000000000000000000000001
größer ist als 2
.
Bash versteht keine Gleitkomma-Arithmetik. Zahlen, die ein Dezimalzeichen enthalten, werden als Zeichenfolgen behandelt.
Verwenden Sie stattdessen awk oder bc.
#!/bin/bash
min=12.45
val=10.35
if [ 1 -eq "$(echo "${val} < ${min}" | bc)" ]
then
min=${val}
fi
echo "$min"
Wenn Sie vorhaben, viele mathematische Operationen durchzuführen, ist es wahrscheinlich besser, sich auf Python oder Perl zu verlassen.
Sie können package num-utils für einfache Manipulationen verwenden ...
Weitere Informationen zu Mathematik finden Sie unter diesem Link. Hier werden verschiedene Optionen beschrieben, z.
Ein Beispiel für numprocess
echo "123.456" | numprocess /+33.267,%2.33777/
# 67.0395291239087
A programs for dealing with numbers from the command line
The 'num-utils' are a set of programs for dealing with numbers from the
Unix command line. Much like the other Unix command line utilities like
grep, awk, sort, cut, etc. these utilities work on data from both
standard in and data from files.
Includes these programs:
* numaverage: A program for calculating the average of numbers.
* numbound: Finds the boundary numbers (min and max) of input.
* numinterval: Shows the numeric intervals between each number in a sequence.
* numnormalize: Normalizes a set of numbers between 0 and 1 by default.
* numgrep: Like normal grep, but for sets of numbers.
* numprocess: Do mathematical operations on numbers.
* numsum: Add up all the numbers.
* numrandom: Generate a random number from a given expression.
* numrange: Generate a set of numbers in a range expression.
* numround: Round each number according to its value.
Hier ist ein bash
Hack ... Er fügt der Ganzzahl führende Nullen hinzu, um einen String-Vergleich von links nach rechts aussagekräftig zu machen. Für diesen speziellen Code ist es erforderlich, dass sowohl min als auch val tatsächlich einen Dezimalpunkt und mindestens eine Dezimalstelle haben.
min=12.45
val=10.35
MIN=0; VAL=1 # named array indexes, for clarity
IFS=.; tmp=($min $val); unset IFS
tmp=($(printf -- "%09d.%s\n" ${tmp[@]}))
[[ ${tmp[VAL]} < ${tmp[MIN]} ]] && min=$val
echo min=$min
Ausgabe:
min=10.35
Für einfache Berechnungen von Gleitkommazahlen (+ - * / und Vergleiche) können Sie awk verwenden.
min=$(echo 12.45 10.35 | awk '{if ($1 < $2) print $1; else print $2}')
Wenn Sie ksh93 oder zsh (nicht bash) haben, können Sie die integrierte Arithmetik Ihrer Shell verwenden, die Gleitkommazahlen unterstützt.
if ((min>val)); then ((val=min)); fi
Weitere Informationen zu Gleitkommaberechnungen finden Sie unter bc . Es funktioniert tatsächlich mit Fixpunktzahlen mit beliebiger Genauigkeit.
Wenn Sie mit Zahlentabellen arbeiten möchten, schlagen Sie R nach ( Beispiel ).
Der Befehl sort
hat eine Option -g
( --general-numeric-sort
), die für Vergleiche mit <
"kleiner als" oder "kleiner als" verwendet werden kann>
"größer als" verwendet werden kann, indem das Minimum oder Maximum ermittelt wird.
Diese Beispiele finden das Minimum:
$ printf '12.45\n10.35\n' | sort -g | head -1
10.35
Es funktioniert mit einer ziemlich allgemeinen Notation von Gleitkommazahlen, wie bei der E-Notation
$ printf '12.45E-10\n10.35\n' | sort -g | head -1
12.45E-10
Beachten Sie E-10
, dass die erste Zahl 0.000000001245
tatsächlich kleiner ist als10.35
.
Der Gleitkomma-Standard IEEE754 definiert einige spezielle Werte. Für diese Vergleiche sind die interessantesten INF
für die Unendlichkeit. Es gibt auch die negative Unendlichkeit; Beides sind im Standard genau definierte Werte.
$ printf 'INF\n10.35\n' | sort -g | head -1
10.35
$ printf '-INF\n10.35\n' | sort -g | head -1
-INF
Um die maximale Verwendung finden sort -gr
statt sort -g
, die Sortierreihenfolge umzukehren:
$ printf '12.45\n10.35\n' | sort -gr | head -1
12.45
Um den <
Vergleich ("kleiner als") zu implementieren , damit er in if
etc verwendet werden kann, vergleichen Sie das Minimum mit einem der Werte. Wenn das Minimum dem als Text verglichenen Wert entspricht, ist es kleiner als der andere Wert:
$ a=12.45; b=10.35
$ [ "$a" = "$(printf "$a\n$b\n" | sort -g | head -1)" ]
$ echo $?
1
$ a=12.45; b=100.35
$ [ "$a" = "$(printf "$a\n$b\n" | sort -g | head -1)" ]
$ echo $?
0
a == min(a, b)
das Gleiche ist wie a <= b
. Es ist erwähnenswert, dass dies nicht unbedingt weniger prüft als wenn. Wenn Sie das tun möchten, müssen Sie a == min(a, b) && a != max(a, b)
in anderen a <= b and not a >= b
Verwenden Sie einfach ksh
( ksh93
genau) oder zsh
, die beide Fließkomma-Arithmetiken unterstützen:
$ cat test.ksh
#!/bin/ksh
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo "$min"
$ ./test.ksh
10.35
Edit: Sorry, ich habe vermisst, ksh93
wurde bereits vorgeschlagen. Wenn ich meine Antwort behalte, um das in der Eröffnungsfrage veröffentlichte Skript zu verdeutlichen, kann es ohne Änderung außerhalb des Shell-Switches verwendet werden.
Edit2: Beachten Sie, ksh93
dass der Inhalt der Variablen mit Ihrem Gebietsschema übereinstimmen muss, dh bei einem französischen Gebietsschema muss ein Komma anstelle eines Punkts verwendet werden:
...
min=12,45
val=10,35
...
Eine robustere Lösung besteht darin, das Gebietsschema am Anfang des Skripts festzulegen, um sicherzustellen, dass es unabhängig vom Gebietsschema des Benutzers funktioniert:
...
export LC_ALL=C
min=12.45
val=10.35
...
.
(also nicht in der halben Welt, in der sich das Dezimaltrennzeichen befindet ,
). zsh
hat dieses Problem nicht.
LC_ALL
bedeutet auch, dass Zahlen nicht im vom Benutzer bevorzugten Format angezeigt (oder eingegeben) werden. Eine möglicherweise bessere Vorgehensweise finden Sie unter unix.stackexchange.com/questions/87745/what-does-lc-all-c-do/… .
.
sowieso ist.
min=$(echo "${min}sa ${val}d la <a p" | dc)
Das benutzt den dc
Rechner, um s
den Wert für $min
im Register zu speichern a
und d
kopiert den Wert von $val
oben auf seinen Hauptausführungsstapel. Dann wird l
der Inhalt von a
oben auf den Stapel gelegt und sieht dann so aus:
${min} ${val} ${val}
Die <
beiden obersten Einträge werden vom Stapel entfernt und verglichen. Der Stack sieht dann so aus:
${val}
Wenn der oberste Eintrag kleiner als der zweithöchste war, wird der Inhalt von nach oben geschoben a
, sodass der Stapel wie folgt aussieht:
${min} ${val}
Sonst macht es nichts und der Stack sieht immer noch so aus:
${val}
Dann wird nur p
der oberste Stapeleintrag gedruckt.
Also für dein Problem:
min=12.45
val=12.35
echo "${min}sa ${val}d la <a p" | dc
###OUTPUT
12.35
Aber:
min=12.45
val=12.55
echo "${min}sa ${val}d la <a p" | dc
###OUTPUT
12.45
Warum nicht alt, gut expr
?
Beispielsyntax:
if expr 1.09 '>' 1.1 1>/dev/null; then
echo 'not greater'
fi
Bei wahren Ausdrücken ist der Exit-Code expr 0, und die Zeichenfolge '1' wird an stdout gesendet. Reverse für falsche Ausdrücke.
Ich habe dies mit GNU und FreeBSD 8 expr überprüft.
expr 1.09 '<' -1.1
wird gedruckt 1
und beendet mit 0
(Erfolg).
Um zu überprüfen, ob zwei (möglicherweise gebrochene) Zahlen in Ordnung sind, sort
ist (vernünftigerweise) Folgendes übertragbar:
min=12.45
val=12.55
if { echo $min ; echo $val ; } | sort -n -c 2>/dev/null
then
echo min is smallest
else
echo val is smallest
fi
Wenn Sie jedoch tatsächlich einen Mindestwert aktualisieren möchten, benötigen Sie keinen if
. Sortieren Sie die Zahlen und verwenden Sie immer die erste (kleinste):
min=12.45
val=12.55
smallest=$({ echo $min ; echo $val ; } | sort -n | head -n 1)
echo $smallest
min=$smallest
Normalerweise mache ich ähnliche Dinge mit eingebettetem Python-Code:
#!/bin/sh
min=12.45
val=10.35
python - $min $val<<EOF
if ($min > $val):
print $min
else:
print $val
EOF
$ min=12.45
$ val=10.35
$ [ "$min" \< "$val" ] && echo $val || echo $min
$ 12.45
$ val=13
$ [ "$min" \< "$val" ] && echo $val || echo $min
$ 13
0.5
und0.06
). Sie sollten ein Tool verwenden, das die Dezimalschreibweise bereits versteht.