bash loop with 0.02 increment

Clash Royale CLAN TAG#URR8PPP
up vote
11
down vote
favorite
I want to make a for loop in bash with 0.02 as increments
I tried
for ((i=4.00;i<5.42;i+=0.02))
do
commands
done
but it didn't work.
bash for floating-point
add a comment |
up vote
11
down vote
favorite
I want to make a for loop in bash with 0.02 as increments
I tried
for ((i=4.00;i<5.42;i+=0.02))
do
commands
done
but it didn't work.
bash for floating-point
9
Bash doesn't do floating point math.
– jordanm
Sep 1 '15 at 14:42
1
increment can be made bybc, but stopping on 4.52 might be tricky. use @roaima suggestion, have auxiliary var with step of 2, and usei=$(echo $tmp_var / 100 | bc)
– Archemar
Sep 1 '15 at 14:43
2
stackoverflow.com/a/12722107
– Pandya
Sep 1 '15 at 15:01
5
You normally don't want to use floats as a loop index. You're accumulating error on each iteration.
– isanae
Sep 1 '15 at 18:48
add a comment |
up vote
11
down vote
favorite
up vote
11
down vote
favorite
I want to make a for loop in bash with 0.02 as increments
I tried
for ((i=4.00;i<5.42;i+=0.02))
do
commands
done
but it didn't work.
bash for floating-point
I want to make a for loop in bash with 0.02 as increments
I tried
for ((i=4.00;i<5.42;i+=0.02))
do
commands
done
but it didn't work.
bash for floating-point
bash for floating-point
edited Nov 25 at 0:57
Rui F Ribeiro
38.3k1476127
38.3k1476127
asked Sep 1 '15 at 14:36
Mehrshad
5613
5613
9
Bash doesn't do floating point math.
– jordanm
Sep 1 '15 at 14:42
1
increment can be made bybc, but stopping on 4.52 might be tricky. use @roaima suggestion, have auxiliary var with step of 2, and usei=$(echo $tmp_var / 100 | bc)
– Archemar
Sep 1 '15 at 14:43
2
stackoverflow.com/a/12722107
– Pandya
Sep 1 '15 at 15:01
5
You normally don't want to use floats as a loop index. You're accumulating error on each iteration.
– isanae
Sep 1 '15 at 18:48
add a comment |
9
Bash doesn't do floating point math.
– jordanm
Sep 1 '15 at 14:42
1
increment can be made bybc, but stopping on 4.52 might be tricky. use @roaima suggestion, have auxiliary var with step of 2, and usei=$(echo $tmp_var / 100 | bc)
– Archemar
Sep 1 '15 at 14:43
2
stackoverflow.com/a/12722107
– Pandya
Sep 1 '15 at 15:01
5
You normally don't want to use floats as a loop index. You're accumulating error on each iteration.
– isanae
Sep 1 '15 at 18:48
9
9
Bash doesn't do floating point math.
– jordanm
Sep 1 '15 at 14:42
Bash doesn't do floating point math.
– jordanm
Sep 1 '15 at 14:42
1
1
increment can be made by
bc, but stopping on 4.52 might be tricky. use @roaima suggestion, have auxiliary var with step of 2, and use i=$(echo $tmp_var / 100 | bc)– Archemar
Sep 1 '15 at 14:43
increment can be made by
bc, but stopping on 4.52 might be tricky. use @roaima suggestion, have auxiliary var with step of 2, and use i=$(echo $tmp_var / 100 | bc)– Archemar
Sep 1 '15 at 14:43
2
2
stackoverflow.com/a/12722107
– Pandya
Sep 1 '15 at 15:01
stackoverflow.com/a/12722107
– Pandya
Sep 1 '15 at 15:01
5
5
You normally don't want to use floats as a loop index. You're accumulating error on each iteration.
– isanae
Sep 1 '15 at 18:48
You normally don't want to use floats as a loop index. You're accumulating error on each iteration.
– isanae
Sep 1 '15 at 18:48
add a comment |
4 Answers
4
active
oldest
votes
up vote
18
down vote
Reading the bash man page gives the following information:
for (( expr1 ; expr2 ; expr3 )) ; do list ; done
First, the arithmetic expression
expr1is evaluated according to the rules described below under ARITHMETIC EVALUATION. [...]
and then we get this section
ARITHMETIC EVALUATION
The shell allows arithmetic expressions to be evaluated, under certain
circumstances (see theletanddeclarebuiltin commands and
Arithmetic Expansion). Evaluation is done in fixed-width integers
with no check for overflow [...]
So it can be clearly seen that you cannot use a for loop with non-integer values.
One solution may be simply to multiply all your loop components by 100, allowing for this where you later use them, like this:
for ((k=400;k<542;k+=2))
do
i=$(bc <<<"scale=2; $k / 100" ) # when k=402 you get i=4.02, etc.
...
done
I think this is the best solution with thek=400;k<542;k+=2it avoids also potential floating point arithmetic troubles.
– Huygens
Sep 1 '15 at 17:39
1
Note that for each iteration in the loop, you create a pipe (to read the output ofbc), fork a process, create a temporary file (for the here-string), executebcin it (which implies loading an executable and shared libraries and initialising them), wait for it and clean-up. Runningbconce to do the loop would be a lot more efficient.
– Stéphane Chazelas
Sep 2 '15 at 9:14
@StéphaneChazelas yes, agreed. But if this is the bottleneck then we're probably writing the code in the wrong language anyway. OOI which is less inefficient (!)?i=$(bc <<< "scale...")ori=$(echo "scale..." | bc)
– roaima
Sep 2 '15 at 9:54
1
From my quick test, the pipe version is faster in zsh (where<<<comes from),bashandksh. Note that switching to another shell thanbashwill give you a better performance boost than using the other syntax anyway.
– Stéphane Chazelas
Sep 2 '15 at 10:00
(and most of the shells that support<<<(zsh, mksh, ksh93, yash) also support floating point arithmetics (zsh,ksh93,yash)).
– Stéphane Chazelas
Sep 2 '15 at 11:52
add a comment |
up vote
17
down vote
Avoid loops in shells.
If you want to do arithmetic, use awk or bc:
awk '
BEGIN
for (i = 4.00; i < 5.42; i+ = 0.02)
print i
'
Or
bc << EOF
for (i = 4.00; i < 5.42; i += 0.02) i
EOF
Note that awk (contrary to bc) works with your processors double floating point number representation (likely IEEE 754 type). As a result, since those numbers are binary approximations of those decimal numbers, you may have some surprises:
$ gawk 'BEGINfor (i=0; i<=0.3; i+=0.1) print i'
0
0.1
0.2
If you add a OFMT="%.17g" you can see the reason for the missing 0.3:
$ gawk 'BEGINOFMT="%.17g"; for (i=0; i<=0.5; i+=0.1) print i'
0
0.10000000000000001
0.20000000000000001
0.30000000000000004
0.40000000000000002
0.5
bc does arbitrary precision so doesn't have this kind of problem.
Note that by default (unless you modify the output format with OFMT or use printf with explicit format specifications), awk uses %.6g for displaying floating point numbers, so would switch to 1e6 and above for floating point numbers above 1,000,000 and truncate the fractional part for high numbers (100000.02 would be displayed as 100000).
If you do really need to use a shell loop, because for instance you want to run specific commands for each iteration of that loop, either use a shell with floating point arithmetic support like zsh, yash or ksh93 or generate the list of values with one command as above (or seq if available) and loop over its output.
Like:
unset -v IFS # configure split+glob for default word splitting
for i in $(seq 4 0.02 5.42); do
something with "$i"
done
Or:
seq 4 0.02 5.42 | while IFS= read i; do
something with "$i"
done
unless you push the limits of your processor floating point numbers, seq handles errors incurred by floating point approximations more gracefully than the awk version above would.
If you don't have seq (a GNU command), you can make a more reliable one as a function like:
seq() # args: first increment last
bc << EOF
for (i = $1; i <= $3; i += $2) i
EOF
That would work better for things like seq 100000000001 0.000000001 100000000001.000000005. Note however that having numbers with arbitrarily high precision won't help much if we're going to pass them to commands which don't support them.
I appreciate use of awk! +1
– Pandya
Sep 1 '15 at 15:05
Why do you need tounset IFSin the first example?
– user1717828
Sep 1 '15 at 23:18
@user1717828, ideally, with that split+glob invocation, we want to split on newline characters. We can do that withIFS=$'n'but that doesn't work in all shells. OrIFS='<a-litteral-newline-here>'but that's not very legible. Or we can split on words instead (space, tab, newline) like you get with the default value of $IFS or if you unset IFS and also works here.
– Stéphane Chazelas
Sep 2 '15 at 7:15
@user1717828: we don't need to mess withIFS, because we know thatseq's output doesn't have spaces that we need to avoid splitting on. It's mostly there to make sure you realize that this example depends onIFS, which might matter for a different list-generating command.
– Peter Cordes
Sep 2 '15 at 8:27
1
@PeterCordes, it's there so we don't need to make any assumption on what IFS was set to beforehand.
– Stéphane Chazelas
Sep 2 '15 at 8:47
add a comment |
up vote
2
down vote
Use "seq" - print a sequence of numbers
seq FIRST INCREMENT LAST
for i in $(seq 4.00 0.02 5.42)
do
echo $i
done
That answer has already been given.
– Stéphane Chazelas
Sep 2 '15 at 9:30
add a comment |
up vote
0
down vote
As others have suggested, you can use bc:
i="4.00"
while [[ "$(bc <<< "$i < 5.42")" == "1" ]]; do
# do something with i
i="$(bc <<< "$i + 0.02")"
done
add a comment |
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
18
down vote
Reading the bash man page gives the following information:
for (( expr1 ; expr2 ; expr3 )) ; do list ; done
First, the arithmetic expression
expr1is evaluated according to the rules described below under ARITHMETIC EVALUATION. [...]
and then we get this section
ARITHMETIC EVALUATION
The shell allows arithmetic expressions to be evaluated, under certain
circumstances (see theletanddeclarebuiltin commands and
Arithmetic Expansion). Evaluation is done in fixed-width integers
with no check for overflow [...]
So it can be clearly seen that you cannot use a for loop with non-integer values.
One solution may be simply to multiply all your loop components by 100, allowing for this where you later use them, like this:
for ((k=400;k<542;k+=2))
do
i=$(bc <<<"scale=2; $k / 100" ) # when k=402 you get i=4.02, etc.
...
done
I think this is the best solution with thek=400;k<542;k+=2it avoids also potential floating point arithmetic troubles.
– Huygens
Sep 1 '15 at 17:39
1
Note that for each iteration in the loop, you create a pipe (to read the output ofbc), fork a process, create a temporary file (for the here-string), executebcin it (which implies loading an executable and shared libraries and initialising them), wait for it and clean-up. Runningbconce to do the loop would be a lot more efficient.
– Stéphane Chazelas
Sep 2 '15 at 9:14
@StéphaneChazelas yes, agreed. But if this is the bottleneck then we're probably writing the code in the wrong language anyway. OOI which is less inefficient (!)?i=$(bc <<< "scale...")ori=$(echo "scale..." | bc)
– roaima
Sep 2 '15 at 9:54
1
From my quick test, the pipe version is faster in zsh (where<<<comes from),bashandksh. Note that switching to another shell thanbashwill give you a better performance boost than using the other syntax anyway.
– Stéphane Chazelas
Sep 2 '15 at 10:00
(and most of the shells that support<<<(zsh, mksh, ksh93, yash) also support floating point arithmetics (zsh,ksh93,yash)).
– Stéphane Chazelas
Sep 2 '15 at 11:52
add a comment |
up vote
18
down vote
Reading the bash man page gives the following information:
for (( expr1 ; expr2 ; expr3 )) ; do list ; done
First, the arithmetic expression
expr1is evaluated according to the rules described below under ARITHMETIC EVALUATION. [...]
and then we get this section
ARITHMETIC EVALUATION
The shell allows arithmetic expressions to be evaluated, under certain
circumstances (see theletanddeclarebuiltin commands and
Arithmetic Expansion). Evaluation is done in fixed-width integers
with no check for overflow [...]
So it can be clearly seen that you cannot use a for loop with non-integer values.
One solution may be simply to multiply all your loop components by 100, allowing for this where you later use them, like this:
for ((k=400;k<542;k+=2))
do
i=$(bc <<<"scale=2; $k / 100" ) # when k=402 you get i=4.02, etc.
...
done
I think this is the best solution with thek=400;k<542;k+=2it avoids also potential floating point arithmetic troubles.
– Huygens
Sep 1 '15 at 17:39
1
Note that for each iteration in the loop, you create a pipe (to read the output ofbc), fork a process, create a temporary file (for the here-string), executebcin it (which implies loading an executable and shared libraries and initialising them), wait for it and clean-up. Runningbconce to do the loop would be a lot more efficient.
– Stéphane Chazelas
Sep 2 '15 at 9:14
@StéphaneChazelas yes, agreed. But if this is the bottleneck then we're probably writing the code in the wrong language anyway. OOI which is less inefficient (!)?i=$(bc <<< "scale...")ori=$(echo "scale..." | bc)
– roaima
Sep 2 '15 at 9:54
1
From my quick test, the pipe version is faster in zsh (where<<<comes from),bashandksh. Note that switching to another shell thanbashwill give you a better performance boost than using the other syntax anyway.
– Stéphane Chazelas
Sep 2 '15 at 10:00
(and most of the shells that support<<<(zsh, mksh, ksh93, yash) also support floating point arithmetics (zsh,ksh93,yash)).
– Stéphane Chazelas
Sep 2 '15 at 11:52
add a comment |
up vote
18
down vote
up vote
18
down vote
Reading the bash man page gives the following information:
for (( expr1 ; expr2 ; expr3 )) ; do list ; done
First, the arithmetic expression
expr1is evaluated according to the rules described below under ARITHMETIC EVALUATION. [...]
and then we get this section
ARITHMETIC EVALUATION
The shell allows arithmetic expressions to be evaluated, under certain
circumstances (see theletanddeclarebuiltin commands and
Arithmetic Expansion). Evaluation is done in fixed-width integers
with no check for overflow [...]
So it can be clearly seen that you cannot use a for loop with non-integer values.
One solution may be simply to multiply all your loop components by 100, allowing for this where you later use them, like this:
for ((k=400;k<542;k+=2))
do
i=$(bc <<<"scale=2; $k / 100" ) # when k=402 you get i=4.02, etc.
...
done
Reading the bash man page gives the following information:
for (( expr1 ; expr2 ; expr3 )) ; do list ; done
First, the arithmetic expression
expr1is evaluated according to the rules described below under ARITHMETIC EVALUATION. [...]
and then we get this section
ARITHMETIC EVALUATION
The shell allows arithmetic expressions to be evaluated, under certain
circumstances (see theletanddeclarebuiltin commands and
Arithmetic Expansion). Evaluation is done in fixed-width integers
with no check for overflow [...]
So it can be clearly seen that you cannot use a for loop with non-integer values.
One solution may be simply to multiply all your loop components by 100, allowing for this where you later use them, like this:
for ((k=400;k<542;k+=2))
do
i=$(bc <<<"scale=2; $k / 100" ) # when k=402 you get i=4.02, etc.
...
done
edited Sep 1 '15 at 15:08
Fiximan
3,143524
3,143524
answered Sep 1 '15 at 14:42
roaima
42.2k550115
42.2k550115
I think this is the best solution with thek=400;k<542;k+=2it avoids also potential floating point arithmetic troubles.
– Huygens
Sep 1 '15 at 17:39
1
Note that for each iteration in the loop, you create a pipe (to read the output ofbc), fork a process, create a temporary file (for the here-string), executebcin it (which implies loading an executable and shared libraries and initialising them), wait for it and clean-up. Runningbconce to do the loop would be a lot more efficient.
– Stéphane Chazelas
Sep 2 '15 at 9:14
@StéphaneChazelas yes, agreed. But if this is the bottleneck then we're probably writing the code in the wrong language anyway. OOI which is less inefficient (!)?i=$(bc <<< "scale...")ori=$(echo "scale..." | bc)
– roaima
Sep 2 '15 at 9:54
1
From my quick test, the pipe version is faster in zsh (where<<<comes from),bashandksh. Note that switching to another shell thanbashwill give you a better performance boost than using the other syntax anyway.
– Stéphane Chazelas
Sep 2 '15 at 10:00
(and most of the shells that support<<<(zsh, mksh, ksh93, yash) also support floating point arithmetics (zsh,ksh93,yash)).
– Stéphane Chazelas
Sep 2 '15 at 11:52
add a comment |
I think this is the best solution with thek=400;k<542;k+=2it avoids also potential floating point arithmetic troubles.
– Huygens
Sep 1 '15 at 17:39
1
Note that for each iteration in the loop, you create a pipe (to read the output ofbc), fork a process, create a temporary file (for the here-string), executebcin it (which implies loading an executable and shared libraries and initialising them), wait for it and clean-up. Runningbconce to do the loop would be a lot more efficient.
– Stéphane Chazelas
Sep 2 '15 at 9:14
@StéphaneChazelas yes, agreed. But if this is the bottleneck then we're probably writing the code in the wrong language anyway. OOI which is less inefficient (!)?i=$(bc <<< "scale...")ori=$(echo "scale..." | bc)
– roaima
Sep 2 '15 at 9:54
1
From my quick test, the pipe version is faster in zsh (where<<<comes from),bashandksh. Note that switching to another shell thanbashwill give you a better performance boost than using the other syntax anyway.
– Stéphane Chazelas
Sep 2 '15 at 10:00
(and most of the shells that support<<<(zsh, mksh, ksh93, yash) also support floating point arithmetics (zsh,ksh93,yash)).
– Stéphane Chazelas
Sep 2 '15 at 11:52
I think this is the best solution with the
k=400;k<542;k+=2 it avoids also potential floating point arithmetic troubles.– Huygens
Sep 1 '15 at 17:39
I think this is the best solution with the
k=400;k<542;k+=2 it avoids also potential floating point arithmetic troubles.– Huygens
Sep 1 '15 at 17:39
1
1
Note that for each iteration in the loop, you create a pipe (to read the output of
bc), fork a process, create a temporary file (for the here-string), execute bc in it (which implies loading an executable and shared libraries and initialising them), wait for it and clean-up. Running bc once to do the loop would be a lot more efficient.– Stéphane Chazelas
Sep 2 '15 at 9:14
Note that for each iteration in the loop, you create a pipe (to read the output of
bc), fork a process, create a temporary file (for the here-string), execute bc in it (which implies loading an executable and shared libraries and initialising them), wait for it and clean-up. Running bc once to do the loop would be a lot more efficient.– Stéphane Chazelas
Sep 2 '15 at 9:14
@StéphaneChazelas yes, agreed. But if this is the bottleneck then we're probably writing the code in the wrong language anyway. OOI which is less inefficient (!)?
i=$(bc <<< "scale...") or i=$(echo "scale..." | bc)– roaima
Sep 2 '15 at 9:54
@StéphaneChazelas yes, agreed. But if this is the bottleneck then we're probably writing the code in the wrong language anyway. OOI which is less inefficient (!)?
i=$(bc <<< "scale...") or i=$(echo "scale..." | bc)– roaima
Sep 2 '15 at 9:54
1
1
From my quick test, the pipe version is faster in zsh (where
<<< comes from), bash and ksh. Note that switching to another shell than bash will give you a better performance boost than using the other syntax anyway.– Stéphane Chazelas
Sep 2 '15 at 10:00
From my quick test, the pipe version is faster in zsh (where
<<< comes from), bash and ksh. Note that switching to another shell than bash will give you a better performance boost than using the other syntax anyway.– Stéphane Chazelas
Sep 2 '15 at 10:00
(and most of the shells that support
<<< (zsh, mksh, ksh93, yash) also support floating point arithmetics (zsh, ksh93, yash)).– Stéphane Chazelas
Sep 2 '15 at 11:52
(and most of the shells that support
<<< (zsh, mksh, ksh93, yash) also support floating point arithmetics (zsh, ksh93, yash)).– Stéphane Chazelas
Sep 2 '15 at 11:52
add a comment |
up vote
17
down vote
Avoid loops in shells.
If you want to do arithmetic, use awk or bc:
awk '
BEGIN
for (i = 4.00; i < 5.42; i+ = 0.02)
print i
'
Or
bc << EOF
for (i = 4.00; i < 5.42; i += 0.02) i
EOF
Note that awk (contrary to bc) works with your processors double floating point number representation (likely IEEE 754 type). As a result, since those numbers are binary approximations of those decimal numbers, you may have some surprises:
$ gawk 'BEGINfor (i=0; i<=0.3; i+=0.1) print i'
0
0.1
0.2
If you add a OFMT="%.17g" you can see the reason for the missing 0.3:
$ gawk 'BEGINOFMT="%.17g"; for (i=0; i<=0.5; i+=0.1) print i'
0
0.10000000000000001
0.20000000000000001
0.30000000000000004
0.40000000000000002
0.5
bc does arbitrary precision so doesn't have this kind of problem.
Note that by default (unless you modify the output format with OFMT or use printf with explicit format specifications), awk uses %.6g for displaying floating point numbers, so would switch to 1e6 and above for floating point numbers above 1,000,000 and truncate the fractional part for high numbers (100000.02 would be displayed as 100000).
If you do really need to use a shell loop, because for instance you want to run specific commands for each iteration of that loop, either use a shell with floating point arithmetic support like zsh, yash or ksh93 or generate the list of values with one command as above (or seq if available) and loop over its output.
Like:
unset -v IFS # configure split+glob for default word splitting
for i in $(seq 4 0.02 5.42); do
something with "$i"
done
Or:
seq 4 0.02 5.42 | while IFS= read i; do
something with "$i"
done
unless you push the limits of your processor floating point numbers, seq handles errors incurred by floating point approximations more gracefully than the awk version above would.
If you don't have seq (a GNU command), you can make a more reliable one as a function like:
seq() # args: first increment last
bc << EOF
for (i = $1; i <= $3; i += $2) i
EOF
That would work better for things like seq 100000000001 0.000000001 100000000001.000000005. Note however that having numbers with arbitrarily high precision won't help much if we're going to pass them to commands which don't support them.
I appreciate use of awk! +1
– Pandya
Sep 1 '15 at 15:05
Why do you need tounset IFSin the first example?
– user1717828
Sep 1 '15 at 23:18
@user1717828, ideally, with that split+glob invocation, we want to split on newline characters. We can do that withIFS=$'n'but that doesn't work in all shells. OrIFS='<a-litteral-newline-here>'but that's not very legible. Or we can split on words instead (space, tab, newline) like you get with the default value of $IFS or if you unset IFS and also works here.
– Stéphane Chazelas
Sep 2 '15 at 7:15
@user1717828: we don't need to mess withIFS, because we know thatseq's output doesn't have spaces that we need to avoid splitting on. It's mostly there to make sure you realize that this example depends onIFS, which might matter for a different list-generating command.
– Peter Cordes
Sep 2 '15 at 8:27
1
@PeterCordes, it's there so we don't need to make any assumption on what IFS was set to beforehand.
– Stéphane Chazelas
Sep 2 '15 at 8:47
add a comment |
up vote
17
down vote
Avoid loops in shells.
If you want to do arithmetic, use awk or bc:
awk '
BEGIN
for (i = 4.00; i < 5.42; i+ = 0.02)
print i
'
Or
bc << EOF
for (i = 4.00; i < 5.42; i += 0.02) i
EOF
Note that awk (contrary to bc) works with your processors double floating point number representation (likely IEEE 754 type). As a result, since those numbers are binary approximations of those decimal numbers, you may have some surprises:
$ gawk 'BEGINfor (i=0; i<=0.3; i+=0.1) print i'
0
0.1
0.2
If you add a OFMT="%.17g" you can see the reason for the missing 0.3:
$ gawk 'BEGINOFMT="%.17g"; for (i=0; i<=0.5; i+=0.1) print i'
0
0.10000000000000001
0.20000000000000001
0.30000000000000004
0.40000000000000002
0.5
bc does arbitrary precision so doesn't have this kind of problem.
Note that by default (unless you modify the output format with OFMT or use printf with explicit format specifications), awk uses %.6g for displaying floating point numbers, so would switch to 1e6 and above for floating point numbers above 1,000,000 and truncate the fractional part for high numbers (100000.02 would be displayed as 100000).
If you do really need to use a shell loop, because for instance you want to run specific commands for each iteration of that loop, either use a shell with floating point arithmetic support like zsh, yash or ksh93 or generate the list of values with one command as above (or seq if available) and loop over its output.
Like:
unset -v IFS # configure split+glob for default word splitting
for i in $(seq 4 0.02 5.42); do
something with "$i"
done
Or:
seq 4 0.02 5.42 | while IFS= read i; do
something with "$i"
done
unless you push the limits of your processor floating point numbers, seq handles errors incurred by floating point approximations more gracefully than the awk version above would.
If you don't have seq (a GNU command), you can make a more reliable one as a function like:
seq() # args: first increment last
bc << EOF
for (i = $1; i <= $3; i += $2) i
EOF
That would work better for things like seq 100000000001 0.000000001 100000000001.000000005. Note however that having numbers with arbitrarily high precision won't help much if we're going to pass them to commands which don't support them.
I appreciate use of awk! +1
– Pandya
Sep 1 '15 at 15:05
Why do you need tounset IFSin the first example?
– user1717828
Sep 1 '15 at 23:18
@user1717828, ideally, with that split+glob invocation, we want to split on newline characters. We can do that withIFS=$'n'but that doesn't work in all shells. OrIFS='<a-litteral-newline-here>'but that's not very legible. Or we can split on words instead (space, tab, newline) like you get with the default value of $IFS or if you unset IFS and also works here.
– Stéphane Chazelas
Sep 2 '15 at 7:15
@user1717828: we don't need to mess withIFS, because we know thatseq's output doesn't have spaces that we need to avoid splitting on. It's mostly there to make sure you realize that this example depends onIFS, which might matter for a different list-generating command.
– Peter Cordes
Sep 2 '15 at 8:27
1
@PeterCordes, it's there so we don't need to make any assumption on what IFS was set to beforehand.
– Stéphane Chazelas
Sep 2 '15 at 8:47
add a comment |
up vote
17
down vote
up vote
17
down vote
Avoid loops in shells.
If you want to do arithmetic, use awk or bc:
awk '
BEGIN
for (i = 4.00; i < 5.42; i+ = 0.02)
print i
'
Or
bc << EOF
for (i = 4.00; i < 5.42; i += 0.02) i
EOF
Note that awk (contrary to bc) works with your processors double floating point number representation (likely IEEE 754 type). As a result, since those numbers are binary approximations of those decimal numbers, you may have some surprises:
$ gawk 'BEGINfor (i=0; i<=0.3; i+=0.1) print i'
0
0.1
0.2
If you add a OFMT="%.17g" you can see the reason for the missing 0.3:
$ gawk 'BEGINOFMT="%.17g"; for (i=0; i<=0.5; i+=0.1) print i'
0
0.10000000000000001
0.20000000000000001
0.30000000000000004
0.40000000000000002
0.5
bc does arbitrary precision so doesn't have this kind of problem.
Note that by default (unless you modify the output format with OFMT or use printf with explicit format specifications), awk uses %.6g for displaying floating point numbers, so would switch to 1e6 and above for floating point numbers above 1,000,000 and truncate the fractional part for high numbers (100000.02 would be displayed as 100000).
If you do really need to use a shell loop, because for instance you want to run specific commands for each iteration of that loop, either use a shell with floating point arithmetic support like zsh, yash or ksh93 or generate the list of values with one command as above (or seq if available) and loop over its output.
Like:
unset -v IFS # configure split+glob for default word splitting
for i in $(seq 4 0.02 5.42); do
something with "$i"
done
Or:
seq 4 0.02 5.42 | while IFS= read i; do
something with "$i"
done
unless you push the limits of your processor floating point numbers, seq handles errors incurred by floating point approximations more gracefully than the awk version above would.
If you don't have seq (a GNU command), you can make a more reliable one as a function like:
seq() # args: first increment last
bc << EOF
for (i = $1; i <= $3; i += $2) i
EOF
That would work better for things like seq 100000000001 0.000000001 100000000001.000000005. Note however that having numbers with arbitrarily high precision won't help much if we're going to pass them to commands which don't support them.
Avoid loops in shells.
If you want to do arithmetic, use awk or bc:
awk '
BEGIN
for (i = 4.00; i < 5.42; i+ = 0.02)
print i
'
Or
bc << EOF
for (i = 4.00; i < 5.42; i += 0.02) i
EOF
Note that awk (contrary to bc) works with your processors double floating point number representation (likely IEEE 754 type). As a result, since those numbers are binary approximations of those decimal numbers, you may have some surprises:
$ gawk 'BEGINfor (i=0; i<=0.3; i+=0.1) print i'
0
0.1
0.2
If you add a OFMT="%.17g" you can see the reason for the missing 0.3:
$ gawk 'BEGINOFMT="%.17g"; for (i=0; i<=0.5; i+=0.1) print i'
0
0.10000000000000001
0.20000000000000001
0.30000000000000004
0.40000000000000002
0.5
bc does arbitrary precision so doesn't have this kind of problem.
Note that by default (unless you modify the output format with OFMT or use printf with explicit format specifications), awk uses %.6g for displaying floating point numbers, so would switch to 1e6 and above for floating point numbers above 1,000,000 and truncate the fractional part for high numbers (100000.02 would be displayed as 100000).
If you do really need to use a shell loop, because for instance you want to run specific commands for each iteration of that loop, either use a shell with floating point arithmetic support like zsh, yash or ksh93 or generate the list of values with one command as above (or seq if available) and loop over its output.
Like:
unset -v IFS # configure split+glob for default word splitting
for i in $(seq 4 0.02 5.42); do
something with "$i"
done
Or:
seq 4 0.02 5.42 | while IFS= read i; do
something with "$i"
done
unless you push the limits of your processor floating point numbers, seq handles errors incurred by floating point approximations more gracefully than the awk version above would.
If you don't have seq (a GNU command), you can make a more reliable one as a function like:
seq() # args: first increment last
bc << EOF
for (i = $1; i <= $3; i += $2) i
EOF
That would work better for things like seq 100000000001 0.000000001 100000000001.000000005. Note however that having numbers with arbitrarily high precision won't help much if we're going to pass them to commands which don't support them.
edited Nov 25 at 7:16
answered Sep 1 '15 at 14:52
Stéphane Chazelas
295k54559902
295k54559902
I appreciate use of awk! +1
– Pandya
Sep 1 '15 at 15:05
Why do you need tounset IFSin the first example?
– user1717828
Sep 1 '15 at 23:18
@user1717828, ideally, with that split+glob invocation, we want to split on newline characters. We can do that withIFS=$'n'but that doesn't work in all shells. OrIFS='<a-litteral-newline-here>'but that's not very legible. Or we can split on words instead (space, tab, newline) like you get with the default value of $IFS or if you unset IFS and also works here.
– Stéphane Chazelas
Sep 2 '15 at 7:15
@user1717828: we don't need to mess withIFS, because we know thatseq's output doesn't have spaces that we need to avoid splitting on. It's mostly there to make sure you realize that this example depends onIFS, which might matter for a different list-generating command.
– Peter Cordes
Sep 2 '15 at 8:27
1
@PeterCordes, it's there so we don't need to make any assumption on what IFS was set to beforehand.
– Stéphane Chazelas
Sep 2 '15 at 8:47
add a comment |
I appreciate use of awk! +1
– Pandya
Sep 1 '15 at 15:05
Why do you need tounset IFSin the first example?
– user1717828
Sep 1 '15 at 23:18
@user1717828, ideally, with that split+glob invocation, we want to split on newline characters. We can do that withIFS=$'n'but that doesn't work in all shells. OrIFS='<a-litteral-newline-here>'but that's not very legible. Or we can split on words instead (space, tab, newline) like you get with the default value of $IFS or if you unset IFS and also works here.
– Stéphane Chazelas
Sep 2 '15 at 7:15
@user1717828: we don't need to mess withIFS, because we know thatseq's output doesn't have spaces that we need to avoid splitting on. It's mostly there to make sure you realize that this example depends onIFS, which might matter for a different list-generating command.
– Peter Cordes
Sep 2 '15 at 8:27
1
@PeterCordes, it's there so we don't need to make any assumption on what IFS was set to beforehand.
– Stéphane Chazelas
Sep 2 '15 at 8:47
I appreciate use of awk! +1
– Pandya
Sep 1 '15 at 15:05
I appreciate use of awk! +1
– Pandya
Sep 1 '15 at 15:05
Why do you need to
unset IFS in the first example?– user1717828
Sep 1 '15 at 23:18
Why do you need to
unset IFS in the first example?– user1717828
Sep 1 '15 at 23:18
@user1717828, ideally, with that split+glob invocation, we want to split on newline characters. We can do that with
IFS=$'n' but that doesn't work in all shells. Or IFS='<a-litteral-newline-here>' but that's not very legible. Or we can split on words instead (space, tab, newline) like you get with the default value of $IFS or if you unset IFS and also works here.– Stéphane Chazelas
Sep 2 '15 at 7:15
@user1717828, ideally, with that split+glob invocation, we want to split on newline characters. We can do that with
IFS=$'n' but that doesn't work in all shells. Or IFS='<a-litteral-newline-here>' but that's not very legible. Or we can split on words instead (space, tab, newline) like you get with the default value of $IFS or if you unset IFS and also works here.– Stéphane Chazelas
Sep 2 '15 at 7:15
@user1717828: we don't need to mess with
IFS, because we know that seq's output doesn't have spaces that we need to avoid splitting on. It's mostly there to make sure you realize that this example depends on IFS, which might matter for a different list-generating command.– Peter Cordes
Sep 2 '15 at 8:27
@user1717828: we don't need to mess with
IFS, because we know that seq's output doesn't have spaces that we need to avoid splitting on. It's mostly there to make sure you realize that this example depends on IFS, which might matter for a different list-generating command.– Peter Cordes
Sep 2 '15 at 8:27
1
1
@PeterCordes, it's there so we don't need to make any assumption on what IFS was set to beforehand.
– Stéphane Chazelas
Sep 2 '15 at 8:47
@PeterCordes, it's there so we don't need to make any assumption on what IFS was set to beforehand.
– Stéphane Chazelas
Sep 2 '15 at 8:47
add a comment |
up vote
2
down vote
Use "seq" - print a sequence of numbers
seq FIRST INCREMENT LAST
for i in $(seq 4.00 0.02 5.42)
do
echo $i
done
That answer has already been given.
– Stéphane Chazelas
Sep 2 '15 at 9:30
add a comment |
up vote
2
down vote
Use "seq" - print a sequence of numbers
seq FIRST INCREMENT LAST
for i in $(seq 4.00 0.02 5.42)
do
echo $i
done
That answer has already been given.
– Stéphane Chazelas
Sep 2 '15 at 9:30
add a comment |
up vote
2
down vote
up vote
2
down vote
Use "seq" - print a sequence of numbers
seq FIRST INCREMENT LAST
for i in $(seq 4.00 0.02 5.42)
do
echo $i
done
Use "seq" - print a sequence of numbers
seq FIRST INCREMENT LAST
for i in $(seq 4.00 0.02 5.42)
do
echo $i
done
answered Sep 2 '15 at 7:47
borzole
211
211
That answer has already been given.
– Stéphane Chazelas
Sep 2 '15 at 9:30
add a comment |
That answer has already been given.
– Stéphane Chazelas
Sep 2 '15 at 9:30
That answer has already been given.
– Stéphane Chazelas
Sep 2 '15 at 9:30
That answer has already been given.
– Stéphane Chazelas
Sep 2 '15 at 9:30
add a comment |
up vote
0
down vote
As others have suggested, you can use bc:
i="4.00"
while [[ "$(bc <<< "$i < 5.42")" == "1" ]]; do
# do something with i
i="$(bc <<< "$i + 0.02")"
done
add a comment |
up vote
0
down vote
As others have suggested, you can use bc:
i="4.00"
while [[ "$(bc <<< "$i < 5.42")" == "1" ]]; do
# do something with i
i="$(bc <<< "$i + 0.02")"
done
add a comment |
up vote
0
down vote
up vote
0
down vote
As others have suggested, you can use bc:
i="4.00"
while [[ "$(bc <<< "$i < 5.42")" == "1" ]]; do
# do something with i
i="$(bc <<< "$i + 0.02")"
done
As others have suggested, you can use bc:
i="4.00"
while [[ "$(bc <<< "$i < 5.42")" == "1" ]]; do
# do something with i
i="$(bc <<< "$i + 0.02")"
done
answered Sep 1 '15 at 14:55
Andy Dalton
5,0541721
5,0541721
add a comment |
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f226803%2fbash-loop-with-0-02-increment%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
9
Bash doesn't do floating point math.
– jordanm
Sep 1 '15 at 14:42
1
increment can be made by
bc, but stopping on 4.52 might be tricky. use @roaima suggestion, have auxiliary var with step of 2, and usei=$(echo $tmp_var / 100 | bc)– Archemar
Sep 1 '15 at 14:43
2
stackoverflow.com/a/12722107
– Pandya
Sep 1 '15 at 15:01
5
You normally don't want to use floats as a loop index. You're accumulating error on each iteration.
– isanae
Sep 1 '15 at 18:48