Hindari loop dalam cangkang.
Jika Anda ingin melakukan aritmatika, gunakan awk
atau bc
:
awk '
BEGIN{
for (i = 4.00; i < 5.42; i+ = 0.02)
print i
}'
Atau
bc << EOF
for (i = 4.00; i < 5.42; i += 0.02) i
EOF
Perhatikan bahwa awk
(bertentangan dengan bc
) bekerja dengan double
representasi nomor floating point prosesor Anda (kemungkinan tipe IEEE 754 ). Akibatnya, karena angka-angka itu adalah perkiraan biner dari angka desimal itu, Anda mungkin memiliki beberapa kejutan:
$ gawk 'BEGIN{for (i=0; i<=0.3; i+=0.1) print i}'
0
0.1
0.2
Jika Anda menambahkan, OFMT="%.17g"
Anda dapat melihat alasan hilangnya 0.3
:
$ gawk 'BEGIN{OFMT="%.17g"; for (i=0; i<=0.5; i+=0.1) print i}'
0
0.10000000000000001
0.20000000000000001
0.30000000000000004
0.40000000000000002
0.5
bc
tidak presisi sewenang-wenang sehingga tidak memiliki masalah seperti ini.
Perhatikan bahwa secara default (kecuali jika Anda memodifikasi format output dengan OFMT
atau menggunakan printf
dengan spesifikasi format eksplisit), awk
digunakan %.6g
untuk menampilkan angka floating point, jadi akan beralih ke 1e6 dan di atasnya untuk angka floating point di atas 1.000.000 dan memotong bagian fraksional untuk angka tinggi (100000.02 akan ditampilkan sebagai 100000).
Jika Anda benar-benar perlu menggunakan shell loop, karena misalnya Anda ingin menjalankan perintah khusus untuk setiap iterasi dari loop itu, baik menggunakan shell dengan dukungan aritmatika floating point seperti zsh
, yash
atau ksh93
atau menghasilkan daftar nilai dengan satu perintah seperti di atas (atau seq
jika tersedia) dan lewati outputnya.
Suka:
unset -v IFS # configure split+glob for default word splitting
for i in $(seq 4 0.02 5.42); do
something with "$i"
done
Atau:
seq 4 0.02 5.42 | while IFS= read i; do
something with "$i"
done
kecuali Anda menekan batas angka floating point prosesor Anda, seq
menangani kesalahan yang ditimbulkan oleh pendekatan floating point lebih anggun daripada awk
versi di atas.
Jika Anda tidak memiliki seq
(perintah GNU), Anda dapat membuat yang lebih dapat diandalkan sebagai fungsi seperti:
seq() { # args: first increment last
bc << EOF
for (i = $1; i <= $3; i += $2) i
EOF
}
Itu akan bekerja lebih baik untuk hal-hal seperti seq 100000000001 0.000000001 100000000001.000000005
. Namun perlu dicatat bahwa memiliki angka dengan presisi tinggi yang sewenang-wenang tidak akan banyak membantu jika kita akan meneruskannya ke perintah yang tidak mendukungnya.