增量为 0.02 的 bash 循环

增量为 0.02 的 bash 循环

我想在 bash 中创建一个 for 循环,以 0.02 作为我尝试过的增量

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

但没有成功。

答案1

避免 shell 中出现循环。

如果你想进行算术运算,请使用awkor 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显示浮点数,因此对于超过 1,000,000 的浮点数将切换到 1e6 及以上,并截断高数字的小数部分 (100000.02将显示为 100000)。

如果您确实需要使用 shell 循环,因为例如您想为该循环的每次迭代运行特定命令,请使用具有浮点算术支持的 shell,例如zshyash或者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。但请注意,如果我们要将它们传递给不支持它们的命令,那么具有任意高精度的数字不会有太大帮助。

答案2

正在阅读bash 手册页给出以下信息:

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

expr1首先,根据下面算术评估中描述的规则评估算术表达式。 [...]

然后我们得到这个部分

算术评估

shell 允许在某些情况下计算算术表达式(请参阅letdeclare内置命令以及算术扩展)。评估以固定宽度整数完成,不检查溢出[...]

因此可以清楚地看出,不能使用for具有非整数值的循环。

一种解决方案可能只是将所有循环组件乘以 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

如果起始值和结束值的有效数字数量相同(本例中为三个),则可以避免调用bcfor 循环的每次迭代,而是使用字符串处理来生成十进制值,

    i="${k%??}.${k#?}"                 # POSIX; when k=402 you get i=4.02, etc.
    i="${k:0:1}.${k:1}"                # bash; when k=402 you get i=4.02, etc.

答案3

使用“seq” - 打印数字序列

seq 第一个增量 最后一个

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

答案4

正如其他人所建议的,您可以使用 bc:

i="4.00"

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

相关内容