Mengapa pembagian dalam aritmatika bash menghitung sebagian besar persentase sebagai 0?

15

Mencoba bash arithemetic untuk skrip, tetapi $etidak memperbarui hingga akhir. Outputnya berbicara sendiri.

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

Tl; DR: Tampilkan 1..5 sebagai persentase.

Keluaran:

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

Kenapa ini?

Cybex
sumber
1
Terkait: Bash hanya memberikan integer sebagai output terlepas dari input saat melakukan perhitungan (Meskipun, tidak seperti masalah yang dijelaskan di sini, itu tidak dapat diselesaikan dengan baik dengan mengatur ulang operasi.)
Eliah Kagan

Jawaban:

24

bashtidak dapat menangani aritmatika non-integer. Ini akan memberi Anda hasil yang benar selama semua ekspresi adalah bilangan bulat. Jadi, Anda perlu menghindari mendapatkan nilai non-integer di suatu tempat dalam perhitungan Anda.

Dalam kasus Anda, ketika Anda mengevaluasi 1 / 5, 2 / 5dll. Itu menciptakan nilai nol integer dalam bash korespondensi dengan beberapa nilai non-integer dan hasilnya keluar menjadi nol sesuai. Precedence of division dan multiplication adalah operator yang sama dan preseden yang sama selalu dieksekusi dari kiri ke kanan karena mereka ditempatkan dalam ekspresi.

Salah satu penyelesaiannya adalah melakukan perkalian terlebih dahulu dan kemudian pembagian sehingga bash tidak harus menangani nilai non-integer. Ekspresi yang dikoreksi adalah,

$ 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
sumber
1
Subekspresi 1/5tidak membuat nilai non-integer . Ini menciptakan nilai integer 0, karena hasil pembagian integer dari 1 oleh 5 adalah 0. Operasi lebih lanjut berhasil menggunakan nilai dari 0. Ini bukan yang dimaksudkan OP, tetapi tidak ada operasi yang menciptakan nilai non-integer dan tidak ada operasi yang gagal.
Eliah Kagan
@EliahKagan terima kasih telah menunjukkan kelemahannya. Diedit.
souravc
16

Bash tidak terlalu baik di aritmatika semacam ini ... Ini masalah Anda:

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

Jika Anda perlu menangani nilai-nilai non-integer, Anda bisa menggunakan 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

(terima kasih kepada @Arronical untuk menunjukkan cara memformat output sebagai integer)

Zanna
sumber
Saya pasti akan melihat ini, terima kasih sekali lagi!
Cybex
1
Anda dapat memangkas .0dari output dengan mengubah $percent gema Anda menjadi ${percent%.*}:)
Arronical
11

Tidak seperti bash, awk menawarkan aritmatika floating point penuh. Sebagai contoh:

$ 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
sumber
5

Mencoba

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

:)

Lihat bagian EVALUASI ARITHMETIK dari:

man bash

Ini hanya mendukung integer.

alfred
sumber