web-dev-qa-db-ja.com

0.02インクリメントのbashループ

私が試した増分として0.02のbashでforループを作成したい

for ((i=4.00;i<5.42;i+=0.02))
do
commands
done

しかし、それはうまくいきませんでした。

11
Mehrshad

bashman page を読み取ると、次の情報が得られます。

for (( expr1 ; expr2 ; expr3 )) ; do list ; done

まず、算術式expr1は、以下の算術評価で説明されているルールに従って評価されます。 [...]

そして、このセクションを取得します

算術評価

シェルを使用すると、特定の状況下で算術式を評価できます(letおよびdeclare組み込みコマンドと算術展開を参照)。評価はオーバーフローのチェックなしで固定幅整数で行われます[...]

したがって、整数値以外ではforループを使用できないことがはっきりとわかります。

1つの解決策は、単純にすべてのループコンポーネントに100を掛けて、後で使用できるようにすることです。

for ((k=400;k<542;k+=2))
do
    i=$(bc <<<"scale=2; $k / 100" )    # when k=402 you get i=4.02, etc.
    ...
done
18
roaima

シェルでのループを避けます。

算術演算を行う場合は、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

awkbcとは対照的に)はプロセッサーで動作することに注意してくださいdouble浮動小数点数表現(おそらく IEEE 754 タイプ)。結果として、これらの数値はこれらの10進数の2進近似であるため、いくつかの驚きがあるかもしれません。

$ 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を使用します。したがって、1,000,000を超える浮動小数点数の場合は1e6以上に切り替わり、大きな数の場合は小数部を切り捨てます(100000.02は100000として表示されます)。

シェルループを実際に使用する必要がある場合、たとえばそのループの反復ごとに特定のコマンドを実行したい場合は、zshyashのような浮動小数点演算サポートを備えたシェルを使用します。またはksh93または上記の1つのコマンド(または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(a GNUコマンド)がない場合は、次のような関数として、より信頼性の高いものを作成できます。

seq() { # args: first increment last
  bc << EOF
    for (i = $1; i <= $3; i += $2) i
EOF
}

それはseq 100000000001 0.000000001 100000000001.000000005のようなものに適しています。ただし、サポートされていないコマンドに渡す場合は、任意の高精度の数値を使用してもあまり効果がないことに注意してください。

18

「seq」を使用-一連の数値を出力します

seq FIRST INCREMENT LAST

for i in $(seq 4.00 0.02 5.42)
do 
  echo $i 
done
2
borzole

他の人が示唆したように、あなたはbcを使うことができます:

i="4.00"

while [[ "$(bc <<< "$i < 5.42")" == "1" ]]; do
    # do something with i
    i="$(bc <<< "$i + 0.02")"
done
0
Andy Dalton