0.02 단위로 bash 루프


11

시도한 증분으로 0.02로 bash에서 for 루프를 만들고 싶습니다.

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

그러나 작동하지 않았습니다.


9
Bash는 부동 소수점 수학을 수행하지 않습니다.
jordanm

1
로 증분 할 수 bc있지만 4.52에서 중지하는 것은 까다로울 수 있습니다. @roaima 제안을 사용하고 2 단계의 보조 변수가 있고 사용i=$(echo $tmp_var / 100 | bc)
Archemar


5
일반적으로 float를 루프 인덱스로 사용하고 싶지 않습니다 . 각 반복마다 오류가 누적됩니다.
isanae

답변:


18

bash 매뉴얼 페이지를 읽으면 다음 정보가 제공됩니다.

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

먼저, 산술 식은 expr1ARITHMETIC EVALUATION에서 아래에 설명 된 규칙에 따라 평가됩니다. [...]

우리는이 섹션을 얻을

산술 평가

쉘을 사용하면 특정 상황에서 산술 표현식을 평가할 수 있습니다 ( 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

나는 이것이 k=400;k<542;k+=2잠재적 인 부동 소수점 산술 문제를 피하는 가장 좋은 해결책이라고 생각합니다 .
Huygens

1
루프의 각 반복에 대해 파이프를 작성하고 (의 출력을 읽음 bc) 프로세스를 분기하고 임시 파일 (here-string 용)을 작성하고 실행 bc하십시오 (실행 파일 및 공유 라이브러리로드 및 초기화하고 기다렸다가 정리하십시오. bc루프를 수행하기 위해 한 번 실행 하면 훨씬 더 효율적입니다.
Stéphane Chazelas

@ StéphaneChazelas 예, 동의했습니다. 그러나 이것이 병목 현상이라면 아마도 어쨌든 잘못된 언어로 코드를 작성하고있을 것입니다. 덜 비효율적 인 OOI (!)? i=$(bc <<< "scale...")또는i=$(echo "scale..." | bc)
roaima

1
내 빠른 테스트에서 파이프 버전은 zsh (에서 <<<오는 곳) bash및 속도가 빠릅니다 ksh. 다른 셸로 전환 bash하면 다른 구문을 사용하는 것보다 성능이 향상됩니다.
Stéphane Chazelas

(그리고 껍질의 대부분을 지원하는 <<<(zsh을, mksh, 경우 ksh93, YASH)도 부동 소수점를 arithmetics를 (지원 zsh, ksh93, yash)).
Stéphane Chazelas

18

쉘에서 루프를 피하십시오.

당신은 산술, 사용하고 싶은 경우 awkbc:

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 유형). 결과적으로 해당 숫자는 10 진수의 이진 근사치이므로 몇 가지 놀라운 점이 있습니다.

$ 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으로 표시됨).

당신은 정말 예를 들어 당신이 그 루프의 각 반복에 대해 특정 명령을 실행하려는 때문에, 쉘 루프를 사용하여, 하나 같이 소수점 산술 지원 부동 쉘을 사용해야하는 경우 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합니다. 그러나 임의로 높은 정밀도의 숫자를 갖는 것은 지원하지 않는 명령에 숫자를 전달하는 경우 큰 도움이되지 않습니다.


awk를 사용해 주셔서 감사합니다! +1
Pandya

unset IFS첫 번째 예에서 왜 필요한 가요?
user1717828

@ user1717828, 이상적으로는 split + glob 호출로 개행 문자로 분할하려고합니다. 우리는 그렇게 할 수 IFS=$'\n'있지만 모든 껍질에서 작동하지는 않습니다. 아니면 IFS='<a-litteral-newline-here>'읽기가 쉽지 않습니다. 또는 기본값 $ IFS를 사용하거나 IFS를 설정 해제하고 여기에서도 작동하는 것처럼 단어 (공백, 탭, 줄 바꿈)로 대신 나눌 수 있습니다.
Stéphane Chazelas

@ user1717828 : 우리는 엉망 일 필요가 없다 IFS. 왜냐하면 우리는 seq출력에 분할을 피할 공간이 없다는 것을 알고 있기 때문이다 . 이 예제가에 의존한다는 것을 알기 위해 주로 존재합니다 IFS. 이는 다른 목록 생성 명령에 중요 할 수 있습니다.
Peter Cordes

1
@PeterCordes, 거기에 있으므로 IFS가 미리 설정되어있는 것에 대해 어떤 가정도 할 필요가 없습니다.
Stéphane Chazelas


0

다른 사람들이 제안했듯이 bc를 사용할 수 있습니다.

i="4.00"

while [[ "$(bc <<< "$i < 5.42")" == "1" ]]; do
    # do something with i
    i="$(bc <<< "$i + 0.02")"
done
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.