Evitare anelli nei gusci.
Se vuoi fare l'aritmetica, usa awk
o bc
:
awk '
BEGIN{
for (i = 4.00; i < 5.42; i+ = 0.02)
print i
}'
O
bc << EOF
for (i = 4.00; i < 5.42; i += 0.02) i
EOF
Si noti che awk
(contrariamente a bc
) funziona con la double
rappresentazione del numero in virgola mobile dei processori (probabilmente tipo IEEE 754 ). Di conseguenza, poiché quei numeri sono approssimazioni binarie di quei numeri decimali, potresti avere alcune sorprese:
$ gawk 'BEGIN{for (i=0; i<=0.3; i+=0.1) print i}'
0
0.1
0.2
Se aggiungi un OFMT="%.17g"
puoi vedere il motivo della scomparsa 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
fa precisione arbitraria, quindi non ha questo tipo di problema.
Si noti che per impostazione predefinita (a meno che non si modifichi il formato di output con OFMT
o si utilizzi printf
con specifiche di formato esplicite), gli awk
utilizzi %.6g
per visualizzare i numeri in virgola mobile, quindi passare a 1e6 e oltre per i numeri in virgola mobile superiori a 1.000.000 e troncare la parte frazionaria per i numeri alti (100000.02 sarebbe visualizzato come 100000).
Se si ha realmente bisogno di utilizzare un ciclo di shell, perché per esempio si desidera eseguire comandi specifici per ogni iterazione di quel ciclo, utilizzare una shell con galleggianti supporto aritmetica punto, come zsh
, yash
o ksh93
o generare l'elenco dei valori con un comando come sopra (o seq
se disponibile) e scorrere sopra la sua uscita.
Piace:
unset -v IFS # configure split+glob for default word splitting
for i in $(seq 4 0.02 5.42); do
something with "$i"
done
O:
seq 4 0.02 5.42 | while IFS= read i; do
something with "$i"
done
a meno che non si spingano i limiti dei numeri in virgola mobile del processore, seq
gestisce gli errori causati dalle approssimazioni in virgola mobile in modo più grazioso rispetto alla awk
versione precedente.
Se non hai seq
(un comando GNU), puoi renderne uno più affidabile come una funzione come:
seq() { # args: first increment last
bc << EOF
for (i = $1; i <= $3; i += $2) i
EOF
}
Funzionerebbe meglio per cose del genere seq 100000000001 0.000000001 100000000001.000000005
. Nota comunque che avere numeri con precisione arbitrariamente alta non sarà di grande aiuto se li passeremo a comandi che non li supportano.