Уникайте петель в оболонках.
Якщо ви хочете займатися арифметикою, використовуйте awk
або bc
:
awk '
BEGIN{
for (i = 4.00; i < 5.42; i+ = 0.02)
print i
}'
Або
bc << EOF
for (i = 4.00; i < 5.42; i += 0.02) i
EOF
Зауважте, що awk
(всупереч bc
) працює з вашими процесорами, double
представлення числа з плаваючою комою (ймовірно, тип IEEE 754 ). Як результат, оскільки ці числа є двійковими наближеннями цих десяткових чисел, у вас можуть виникнути деякі сюрпризи:
$ gawk 'BEGIN{for (i=0; i<=0.3; i+=0.1) print i}'
0
0.1
0.2
Якщо ви додасте, OFMT="%.17g"
ви можете побачити причину відсутності 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
робить довільну точність, тому не має подібних проблем.
Зауважте, що за замовчуванням (якщо ви не змінюєте формат виводу за допомогою OFMT
або не використовуєте printf
з явними специфікаціями формату), awk
використовується %.6g
для відображення чисел з плаваючою комою, тому перемикається на 1e6 і вище для чисел з плаваючою комою вище 1 000 000 і обрізає дробову частину для високих чисел (100000.02 відображатиметься як 100000).
Якщо вам дійсно потрібно використовувати цикл оболонки, тому що, наприклад, ви хочете виконати конкретні команди для кожної ітерації цього циклу, або використовуйте оболонку з арифметичною підтримкою з плаваючою комою zsh
, yash
або, ksh93
або генеруйте список значень однією командою, як вище (або seq
за наявності) та переведіть на її вихід.
Люблю:
unset -v IFS # configure split+glob for default word splitting
for i in $(seq 4 0.02 5.42); do
something with "$i"
done
Або:
seq 4 0.02 5.42 | while IFS= read i; do
something with "$i"
done
якщо ви не натискаєте обмеження чисел плаваючої точки на вашому процесорі, seq
виправляєте помилки, нанесені наближеннями плаваючої точки, більш витончено, ніж це awk
було б у версії, наведеній вище.
Якщо у вас немає seq
(команда GNU), ви можете зробити більш надійний як функцію, наприклад:
seq() { # args: first increment last
bc << EOF
for (i = $1; i <= $3; i += $2) i
EOF
}
Це було б краще для таких речей seq 100000000001 0.000000001 100000000001.000000005
. Однак зауважте, що наявність чисел з довільно високою точністю не допоможе багато, якщо ми збираємось передавати їх командам, які їх не підтримують.