it-swarm.com.de

Wie verwende ich Gleitkommadivision in Bash?

Ich versuche, zwei Bildbreiten in einem Bash-Skript zu unterteilen, aber bash gibt mir 0 als Ergebnis:

RESULT=$(($IMG_WIDTH/$IMG2_WIDTH))

Ich habe den Bash-Leitfaden studiert und weiß, dass ich bc verwenden sollte. In allen Beispielen im Internet wird bc verwendet. In echo habe ich versucht, dasselbe in meine SCALE zu setzen, aber es hat nicht funktioniert.

Hier ist das Beispiel, das ich in den Tutorials gefunden habe:

echo "scale=2; ${userinput}" | bc 

Wie kann ich Bash dazu bringen, mir einen Float wie 0.5 zu geben?

178
Medya Gh

Du kannst nicht bash only macht ganze Zahlen; Sie müssen an ein Tool wie bc delegieren.

192

du kannst das:

bc <<< 'scale=2; 100/3'
33.33

UPDATE20130926: Sie können Folgendes verwenden:

bc -l <<< '100/3' # saves a few hits
153
aayoubi

bash

Wie bereits von anderen angemerkt, unterstützt bash keine Gleitkomma-Arithmetik, obwohl Sie es mit einem bestimmten dezimalen Trick, z. mit zwei Dezimalzahlen:

echo $(( 100 * 1 / 3 )) | sed 's/..$/.&/'

Ausgabe:

.33

Siehe Nilfred answer für einen ähnlichen, aber präziseren Ansatz.

Alternativen

Neben den genannten bc- und awk-Alternativen gibt es auch folgende:

clisp

clisp -x '(/ 1.0 3)'

mit aufgeräumter Ausgabe:

clisp --quiet -x '(/ 1.0 3)'

oder durch stdin:

echo '(/ 1.0 3)' | clisp --quiet | tail -n1

dc

echo 2k 1 3 /p | dc

genius CLI-Rechner

echo 1/3.0 | genius

gnuplot

echo 'pr 1/3.' | gnuplot

jq

echo 1/3 | jq -nf /dev/stdin

Oder:

jq -n 1/3

ksh

echo 'print $(( 1/3. ))' | ksh

lua

lua -e 'print(1/3)'

oder durch stdin:

echo 'print(1/3)' | lua

maxima

echo '1/3,numer;' | maxima

mit aufgeräumter Ausgabe:

echo '1/3,numer;' | maxima --quiet | sed -En '2s/[^ ]+ [^ ]+ +//p'

knoten

echo 1/3 | node -p

oktave

echo 1/3 | octave

Perl

echo print 1/3 | Perl

python

echo print 1/3. | python

R

echo 1/3 | R --no-save

mit aufgeräumter Ausgabe:

echo 1/3 | R --Vanilla --quiet | sed -n '2s/.* //p'

Rubin

echo print 1/3.0 | Ruby

wcalc

echo 1/3 | wcalc

Mit aufgeräumter Ausgabe:

echo 1/3 | wcalc | tr -d ' ' | cut -d= -f2

zsh

echo 'print $(( 1/3. ))' | zsh

Andere Quellen

Stéphane Chazelashat eine ähnliche Frage beantwortet über auf Unix.SX.

97
Thor

Die Antwort von Marvin ein wenig verbessern:

RESULT=$(awk "BEGIN {printf \"%.2f\",${IMG_WIDTH}/${IMG2_WIDTH}}")

bc kommt nicht immer als installiertes Paket.

35
adrianlzt

Sie können bc mit der Option -l (der Buchstabe L) verwenden.

RESULT=$(echo "$IMG_WIDTH/$IMG2_WIDTH" | bc -l)
25
user1314742

Als Alternative zu bc können Sie awk in Ihrem Skript verwenden. 

Zum Beispiel:

echo "$IMG_WIDTH $IMG2_WIDTH" | awk '{printf "%.2f \n", $1/$2}'

"% .2f" weist die printf-Funktion an, eine Gleitkommazahl mit zwei Ziffern nach der Dezimalstelle zurückzugeben. Ich habe das Echo verwendet, um die Variablen als Felder zu übergeben, da awk korrekt arbeitet. "$ 1" und "$ 2" beziehen sich auf die ersten und zweiten Felder, die in awk eingegeben werden. 

Sie können das Ergebnis auch als andere Variable speichern:

RESULT = `echo ...`
24
marvin

Es ist die perfekte Zeit, um zsh, einen (fast) bash-Supersatz, auszuprobieren, mit vielen zusätzlichen Nice-Funktionen, einschließlich Gleitkomma-Mathematik. So würde Ihr Beispiel in zsh aussehen:

% IMG_WIDTH=1080
% IMG2_WIDTH=640
% result=$((IMG_WIDTH*1.0/IMG2_WIDTH))
% echo $result
1.6875

Dieser Beitrag kann Ihnen helfen: bash - Wert für zsh für den gelegentlichen Gebrauch?

17
Penghe Geng

Nun, vor dem Float war eine Zeit, in der eine dezimale Logik verwendet wurde:

IMG_WIDTH=100
IMG2_WIDTH=3
RESULT=$((${IMG_WIDTH}00/$IMG2_WIDTH))
echo "${RESULT:0:-2}.${RESULT: -2}"
33.33

Letzte Zeile ist ein Bashim. Wenn Sie Bash nicht verwenden, versuchen Sie stattdessen den folgenden Code:

IMG_WIDTH=100
IMG2_WIDTH=3
INTEGER=$(($IMG_WIDTH/$IMG2_WIDTH))
DECIMAL=$(tail -c 3 <<< $((${IMG_WIDTH}00/$IMG2_WIDTH)))
RESULT=$INTEGER.$DECIMAL
echo $RESULT
33.33

Der Grund hinter dem Code ist: Multipliziere mit 100 vor der Division, um 2 Dezimalstellen zu erhalten.

12
Nilfred

Es ist nicht wirklich Gleitkomma, aber wenn Sie etwas wollen, das mehr als ein Ergebnis in einem Aufruf von bc festlegt ...

source /dev/stdin <<<$(bc <<< '
d='$1'*3.1415926535897932384626433832795*2
print "d=",d,"\n"
a='$1'*'$1'*3.1415926535897932384626433832795
print "a=",a,"\n"
')

echo bc radius:$1 area:$a diameter:$d

berechnet die Fläche und den Durchmesser eines Kreises, dessen Radius in $ 1 angegeben ist 

4
user3210339

Es gibt Szenarien, in denen Sie bc nicht verwenden können, da es einfach nicht vorhanden ist, wie in einigen reduzierten Versionen von busybox oder eingebetteten Systemen. In jedem Fall ist die Begrenzung der äußeren Abhängigkeiten immer eine gute Sache, so dass Sie immer Nullen zu der Zahl hinzufügen können, die durch (Numerator) geteilt wird. Dies ist dasselbe wie das Multiplizieren mit einer Potenz von 10 (Sie sollten eine Potenz von 10 entsprechend wählen die Genauigkeit, die Sie benötigen), wodurch die Division eine Ganzzahl ausgibt. Wenn Sie diese Ganzzahl haben, behandeln Sie sie als String und positionieren Sie den Dezimalpunkt (Verschieben von rechts nach links) entsprechend der Zehnerpotenz, mit der Sie den Zähler multipliziert haben. Dies ist eine einfache Methode, um Float-Ergebnisse zu erhalten, indem nur ganze Zahlen verwendet werden.

4
Daniel J.

Wenn Sie die Variante Ihrer Präferenz gefunden haben, können Sie sie auch in eine Funktion einbinden.

Hier füge ich etwas bashism in eine div-Funktion ein:

Einzeiler:

function div { local _d=${3:-2}; local _n=0000000000; _n=${_n:0:$_d}; local _r=$(($1$_n/$2)); _r=${_r:0:-$_d}.${_r: -$_d}; echo $_r;}

Oder mehrzeilig:

function div {
  local _d=${3:-2}
  local _n=0000000000
  _n=${_n:0:$_d}
  local _r=$(($1$_n/$2))
  _r=${_r:0:-$_d}.${_r: -$_d}
  echo $_r
}

Jetzt hast du die Funktion

div <dividend> <divisor> [<precision=2>]

und benutze es gerne

> div 1 2
.50

> div 273 123 5
2.21951

> x=$(div 22 7)
> echo $x
3.14
2
bebbo

ich weiß, es ist alt, aber zu verlockend. Die Antwort lautet also: Sie können nicht ... aber Sie können. Lass uns das versuchen:

$IMG_WIDTH=1024
$IMG2_WIDTH=2048

$RATIO="$(( IMG_WIDTH / $IMG2_WIDTH )).$(( (IMG_WIDTH * 100 / IMG2_WIDTH) % 100 ))

auf diese Weise erhalten Sie 2 Stellen nach dem Punkt, abgeschnitten (nennen Sie es Abrunden nach unten, haha) in reiner Bash (keine Notwendigkeit, andere Prozesse zu starten). Wenn Sie nach dem Punkt nur eine Ziffer benötigen, multiplizieren Sie natürlich mit 10 und machen modulo 10.

was das macht:

  • erstes $ ((...)) führt eine Ganzzahldivision durch;
  • second $ ((...)) teilt eine Zahl in 100-mal größere Zahlen auf und verschiebt dabei im Wesentlichen die 2 Stellen links vom Punkt. Mit (%) erhalten Sie dann nur diese 2 Stellen, indem Sie modulo ausführen.

bonus track: bc version x 1000 hat auf meinem laptop 1,8 sekunden gedauert, während die pure bash 0,016 sekunden gedauert hat.

Während Sie in Bash keine Gleitkommadivision verwenden können, können Sie Festkommadivision verwenden. Alles, was Sie tun müssen, ist, Ihre ganzen Zahlen mit einer Potenz von 10 zu multiplizieren, den ganzzahligen Teil abzuteilen und eine modulo-Operation zu verwenden, um den gebrochenen Teil zu erhalten. Rundung nach Bedarf.

#!/bin/bash

n=$1
d=$2

# because of rounding this should be 10^{i+1}
# where i is the number of decimal digits wanted
i=4
P=$((10**(i+1)))
Pn=$(($P / 10))
# here we 'fix' the decimal place, divide and round tward zero
t=$(($n * $P / $d + ($n < 0 ? -5 : 5)))
# then we print the number by dividing off the interger part and
# using the modulo operator (after removing the rounding digit) to get the factional part.
printf "%d.%0${i}d\n" $(($t / $P)) $(((t < 0 ? -t : t) / 10 % $Pn))
1

Verwenden Sie calc. Es ist das einfachste Beispiel, das ich gefunden habe:

calc 1 + 1

 2

berechnet 1/10

 0.1
1
Camila Pereira

hier ist der Befehl awk: -F = Feldtrennzeichen == +

echo "2.1+3.1" |  awk -F "+" '{print ($1+$2)}'
0
editinit