Dlaczego podział na arytmetykę bash oblicza większość wartości procentowych jako 0?

15

Próba arytmetyki bash dla skryptu, ale $enie jest aktualizowana do końca. Wynik mówi sam za siebie.

max=5
for e in $(seq 1 1 $max); do 
    percent=$(( $e/$max*100 ))
    echo "echo $e / $max : = $percent"
done

Tl; DR: Wyświetla 1..5 jako procent.

Wynik :

echo 1 / 5 : = 0
echo 2 / 5 : = 0
echo 3 / 5 : = 0
echo 4 / 5 : = 0
echo 5 / 5 : = 100

Dlaczego to?

Cybex
źródło
1
Powiązane: Bash podaje tylko liczbę całkowitą jako wynik niezależnie od danych wejściowych podczas wykonywania obliczeń (chociaż, w przeciwieństwie do opisanego tutaj problemu, nie można tego dobrze rozwiązać poprzez zmianę kolejności operacji.)
Eliah Kagan

Odpowiedzi:

24

bashnie może obsługiwać arytmetyki niecałkowitej. Zapewni to poprawny wynik, o ile wszystkie wyrażenia są liczbami całkowitymi. Musisz więc unikać uzyskania wartości innej niż całkowita gdzieś w swoich obliczeniach.

W przypadku, gdy oceniający 1 / 5, 2 / 5itp tworzy liczbę całkowitą zero wartości w correspondings bash do pewnych wartości nie jest liczbą całkowitą, a wyniki wychodzą na zero odpowiednio. Pierwszeństwo dzielenia i mnożenia są takie same, a te same operatory poprzedzające są zawsze wykonywane od lewej do prawej, gdy są umieszczane w wyrażeniu.

Można obejść najpierw mnożenie, a następnie dzielenie, aby bash nigdy nie musiał obsługiwać wartości niecałkowitych. Skorygowanym wyrażeniem będzie:

$ max=5; for e in $(seq 1 1 $max); do percent=$(( $e*100/$max )); echo "echo $e / $max : = $percent"; done
echo 1 / 5 : = 20
echo 2 / 5 : = 40
echo 3 / 5 : = 60
echo 4 / 5 : = 80
echo 5 / 5 : = 100
souravc
źródło
1
Podwyrażenie 1/5nie tworzy wartości innej niż całkowita . Tworzy wartość całkowitą 0, ponieważ wynikiem dzielenia liczb całkowitych 1 przez 5 jest 0. Dalsze operacje z powodzeniem wykorzystują tę wartość 0. To nie jest zamierzenie OP, ale żadna operacja nie tworzy wartości innej niż całkowita i żadna operacja nie kończy się niepowodzeniem.
Eliah Kagan
@EliahKagan dziękuję za zwrócenie uwagi na wadę. Edytowane.
souravc,
16

Bash nie radzi sobie zbyt dobrze w tego rodzaju arytmetyki ... Oto twój problem:

$ echo $((1/5))
0
$ echo $((2/5))
0
$ echo $((4/5))
0
$ echo $((4/5))
0

Jeśli potrzebujesz obsługi wartości niecałkowitych, możesz użyć bc

$ max=5; for e in $(seq 1 1 "$max"); do percent=$(bc <<< "scale=1 ; $e/$max*100") ; echo "echo $e / $max : = ${percent%.*}"; done
echo 1 / 5 : = 20
echo 2 / 5 : = 40
echo 3 / 5 : = 60
echo 4 / 5 : = 80
echo 5 / 5 : = 100

(dzięki @Arronical za wskazanie, jak sformatować dane wyjściowe jako liczby całkowite)

Zanna
źródło
Na pewno się temu przyjrzę, jeszcze raz dziękuję!
Cybex,
1
Możesz przyciąć .0wyjście, zmieniając $percent echo na ${percent%.*}:)
Arronical
11

W przeciwieństwie do bash, awk oferuje pełną arytmetykę zmiennoprzecinkową. Na przykład:

$ awk -v max=5 'BEGIN{for (e=1;e<=max;e++) print "echo " e " / " max " : = " 100*e/max}' 
echo 1 / 5 : = 20
echo 2 / 5 : = 40
echo 3 / 5 : = 60
echo 4 / 5 : = 80
echo 5 / 5 : = 100
John1024
źródło
5

Próbować

percent=$(( $e*100/$max ))

:)

Patrz sekcja OCENA ARYTMETYCZNA:

man bash

Obsługuje tylko liczbę całkowitą.

alfred
źródło