쉘 스크립트에서 부동 소수점 숫자와 비교하는 방법


22

쉘 스크립트에서 두 개의 부동 소수점 숫자를 비교하고 싶습니다. 다음 코드가 작동하지 않습니다.

#!/bin/bash   
min=12.45
val=10.35    
if (( $val < $min )) ; then    
  min=$val
fi
echo $min 

답변:


5

정수 부분과 분수 부분을 별도로 확인할 수 있습니다.

#!/bin/bash
min=12.45
val=12.35    
if (( ${val%%.*} < ${min%%.*} || ( ${val%%.*} == ${min%%.*} && ${val##*.} < ${min##*.} ) )) ; then    
    min=$val
fi
echo $min

주석에서 언급했듯이 두 숫자에 소수 부분이 있고 두 소수 부분에 동일한 자릿수가있는 경우에만 작동합니다. 다음은 정수 또는 분수 및 모든 bash 연산자에서 작동하는 버전입니다.

#!/bin/bash
shopt -s extglob
fcomp() {
    local oldIFS="$IFS" op=$2 x y digitx digity
    IFS='.' x=( ${1##+([0]|[-]|[+])}) y=( ${3##+([0]|[-]|[+])}) IFS="$oldIFS"
    while [[ "${x[1]}${y[1]}" =~ [^0] ]]; do
        digitx=${x[1]:0:1} digity=${y[1]:0:1}
        (( x[0] = x[0] * 10 + ${digitx:-0} , y[0] = y[0] * 10 + ${digity:-0} ))
        x[1]=${x[1]:1} y[1]=${y[1]:1} 
    done
    [[ ${1:0:1} == '-' ]] && (( x[0] *= -1 ))
    [[ ${3:0:1} == '-' ]] && (( y[0] *= -1 ))
    (( ${x:-0} $op ${y:-0} ))
}

for op in '==' '!=' '>' '<' '<=' '>='; do
    fcomp $1 $op $2 && echo "$1 $op $2"
done

4
많은 작업이 없으면 해결할 수 없습니다 ( 0.5와 비교해보십시오 0.06). 이미 십진 표기법을 이해하는 도구를 사용하는 것이 좋습니다.
Gilles 'SO- 악한 중지

감사합니다 Gilles는 이전 버전보다 더 일반적으로 작동하도록 업데이트했습니다.
ata

참고는 그 말한다 1.00000000000000000000000001보다 크다 2.
Stéphane Chazelas

스테판이 맞습니다. bash의 숫자 표현에 비트 제한이 있기 때문입니다. 물론, 당신이 더 고통을 원한다면 당신은 자신의 표현을 사용할 수 있습니다 .... :)
ata

35

Bash는 부동 소수점 산술을 이해하지 못합니다. 소수점을 포함하는 숫자를 문자열로 처리합니다.

대신 awk 또는 bc를 사용하십시오.

#!/bin/bash

min=12.45
val=10.35

if [ 1 -eq "$(echo "${val} < ${min}" | bc)" ]
then  
    min=${val}
fi

echo "$min"

많은 수학 연산을 수행하려면 파이썬이나 펄에 의존하는 것이 좋습니다.


12

간단한 조작을 위해 패키지 num-utils 를 사용할 수 있습니다 ...

더 심각한 수학에 대해서는 이 링크를 참조하십시오 . 예를 들어 몇 가지 옵션을 설명합니다.

  • R / Rscript (GNU R 통계 계산 및 그래픽 시스템)
  • 옥타브 (대부분 Matlab 호환)
  • bc (GNU bc 임의 정밀도 계산기 언어)

의 예 numprocess

echo "123.456" | numprocess /+33.267,%2.33777/
# 67.0395291239087  

A programs for dealing with numbers from the command line

The 'num-utils' are a set of programs for dealing with numbers from the
Unix command line. Much like the other Unix command line utilities like
grep, awk, sort, cut, etc. these utilities work on data from both
standard in and data from files.

Includes these programs:
 * numaverage: A program for calculating the average of numbers.
 * numbound: Finds the boundary numbers (min and max) of input.
 * numinterval: Shows the numeric intervals between each number in a sequence.
 * numnormalize: Normalizes a set of numbers between 0 and 1 by default.
 * numgrep: Like normal grep, but for sets of numbers.
 * numprocess: Do mathematical operations on numbers.
 * numsum: Add up all the numbers.
 * numrandom: Generate a random number from a given expression.
 * numrange: Generate a set of numbers in a range expression.
 * numround: Round each number according to its value.

여기에 bash해킹이 있습니다 ... 문자열을 왼쪽에서 오른쪽으로 비교하기 위해 정수에 선행 0을 추가합니다. 이 특정 코드 조각은 minval 모두 실제로 소수점과 하나 이상의 10 진수를 가져야합니다.

min=12.45
val=10.35

MIN=0; VAL=1 # named array indexes, for clarity
IFS=.; tmp=($min $val); unset IFS 
tmp=($(printf -- "%09d.%s\n" ${tmp[@]}))
[[ ${tmp[VAL]} < ${tmp[MIN]} ]] && min=$val
echo min=$min

산출:

min=10.35

10

부동 소수점 숫자 (+-* / 및 비교)에 대한 간단한 계산을 위해 awk를 사용할 수 있습니다.

min=$(echo 12.45 10.35 | awk '{if ($1 < $2) print $1; else print $2}')

또는 ksh93 또는 zsh (bash가 아닌)를 사용하는 경우 부동 소수점 숫자를 지원하는 쉘의 내장 산술을 사용할 수 있습니다.

if ((min>val)); then ((val=min)); fi

고급 부동 소수점 계산에 대해서는 bc를 찾아보십시오 . 실제로 임의의 정밀도 수정 점 번호에서 작동합니다.

숫자 표에 대해 작업하려면 R ( )을 찾으십시오 .


6

숫자 정렬 사용

이 명령 sort에는 최소값 또는 최대 값을 찾아 "보다 작음"또는 "보다 큼" 을 비교하는 데 사용할 수 있는 옵션 -g( --general-numeric-sort)이 있습니다 .<>

이 예제는 최소값을 찾습니다.

$ printf '12.45\n10.35\n' | sort -g | head -1
10.35

전자 표기법 지원

전자 표기법과 같이 부동 소수점 숫자의 일반적인 표기법으로 작동합니다.

$ printf '12.45E-10\n10.35\n' | sort -g | head -1
12.45E-10

를 참고 E-10첫 번째 숫자를 만드는 0.000000001245미만 참 10.35.

무한대와 비교 가능

부동 소수점 표준 인 IEEE754 는 일부 특수 값을 정의합니다. 이러한 비교에서 흥미로운 것은 INF무한대입니다. 음의 무한대도 있습니다. 둘 다 표준에서 잘 정의 된 값입니다.

$ printf 'INF\n10.35\n' | sort -g | head -1
10.35
$ printf '-INF\n10.35\n' | sort -g | head -1
-INF

최대 사용 찾으려면 sort -gr대신을 sort -g정렬 순서를 반대로 :

$ printf '12.45\n10.35\n' | sort -gr | head -1
12.45

비교 조작

<( "보다 작음") 비교 를 구현하려면 if등을 사용할 수 있으므로 최소값 중 하나와 비교하십시오. 텍스트와 비교 하여 최소값이 값과 같으면 다른 값보다 작습니다.

$ a=12.45; b=10.35                                    
$ [ "$a" = "$(printf "$a\n$b\n" | sort -g | head -1)" ]
$ echo $?
1
$ a=12.45; b=100.35                                    
$ [ "$a" = "$(printf "$a\n$b\n" | sort -g | head -1)" ]
$ echo $?                                              
0

좋은 팁! 난 정말 당신의 통찰력 등을 검사하는 것으로 a == min(a, b)동일하다 a <= b. 이것이 덜 엄격하게 검사하지 않는다는 점은 주목할 가치가 있습니다. 당신이하고 싶은 경우에, 당신은 확인해야 할 a == min(a, b) && a != max(a, b)otherwords에서,a <= b and not a >= b
데이브

3

부동 소수점 산술을 기본적으로 지원하는 ksh( ksh93정확하게) 또는를 사용하십시오 zsh.

$ cat test.ksh
#!/bin/ksh 
min=12.45
val=10.35    
if (( $val < $min )) ; then    
  min=$val
fi
echo "$min"
$ ./test.ksh
10.35

편집 : 미안, ksh93이미 보고 싶었습니다 . 첫 번째 질문에 게시 된 스크립트를 명확하게하기 위해 내 대답을 유지하면 셸 스위치 외부에서 변경하지 않고 사용할 수 있습니다.

Edit2 : ksh93변수 내용이 로케일과 일치해야합니다 (예 : 프랑스어 로케일의 경우 점 대신 쉼표를 사용해야 함).

...
min=12,45
val=10,35
...

보다 강력한 해결책은 스크립트 시작 부분에 사용자의 로캘에 관계없이 작동하도록 로캘을 설정하는 것입니다.

...
export LC_ALL=C
min=12.45
val=10.35
...

위의 ksh93 스크립트는 소수점 구분 기호가있는 로케일에서만 작동 .하므로 소수점 구분 기호가 있는 세계에서는 절반이 아닙니다 ,. zsh그 문제가 없습니다.
Stéphane Chazelas

실제로, 그 점을 명확히하기 위해 편집 된 답변.
jlliagre

사용자가를 설정 한 경우 LC_NUMERIC 설정이 작동하지 않습니다. LC_ALL즉, 숫자가 사용자가 선호하는 형식으로 표시 (또는 입력)되지 않습니다. 더 나은 접근 방법 은 unix.stackexchange.com/questions/87745/what-does-lc-all-c-do/… 를 참조하십시오 .
Stéphane Chazelas

@ StéphaneChazelas는 LC_NUMERIC 문제를 해결했습니다. OP 스크립트 구문이 주어지면 선호하는 구분 기호가 .어쨌든 가정 합니다.
jlliagre

예. 그러나 스크립트 작성자의 로캘이 아니라 스크립트 사용자의 로캘입니다. 스크립트 작성자는 현지화 및 부작용을 고려해야합니다.
Stéphane Chazelas

1
min=$(echo "${min}sa ${val}d la <a p" | dc)

사용하는 dc 을 계산기 s의 값을 찢고 $min레지스터 ad값을 uplicates $val주요 실행 스택의 상단에. 그런 다음 l내용을 a스택 상단에 흘려 보냅니다 .

${min} ${val} ${val}

<스택은 스택에서 상위 2 개 항목을 팝하여 비교합니다. 따라서 스택은 다음과 같습니다.

${val}

맨 위 항목이 맨 위 항목보다 작 으면 맨 위에 내용을 푸시 a하므로 스택은 다음과 같습니다.

${min} ${val}

그렇지 않으면 아무것도하지 않으며 스택은 여전히 ​​다음과 같습니다.

${val} 

그런 다음 p상단 스택 항목을 헹구십시오.

그래서 당신의 문제를 위해 :

min=12.45
val=12.35
echo "${min}sa ${val}d la <a p" | dc

###OUTPUT

12.35

그러나:

min=12.45
val=12.55
echo "${min}sa ${val}d la <a p" | dc

###OUTPUT

12.45

0

오래되고 좋은 것을 사용하지 않는 이유 expr 않습니까?

구문 예 :

if expr 1.09 '>' 1.1 1>/dev/null; then
    echo 'not greater'
fi

대한 진정한 표현, EXPR 종료 코드는 표준 출력으로 전송 문자열을 '1'로, 0입니다. 반대 잘못된 표현.

나는 이것을 GNU와 FreeBSD 8 expr로 점검했다.


GNU expr은 정수에 대한 산술 비교 만 지원합니다. 귀하의 예는 음수에서는 실패 할 사전 식 비교를 사용합니다. 예를 들어 (성공)으로 expr 1.09 '<' -1.1인쇄 1하고 종료 0합니다.
Adrian Günter

0

두 개의 (아마도 소수) 숫자가 순서대로 있는지 확인하려면 sort(합리적으로) 이식 가능합니다.

min=12.45
val=12.55
if { echo $min ; echo $val ; } | sort -n -c 2>/dev/null
then
  echo min is smallest
else
  echo val is smallest
fi

그러나 실제로 최소값을 업데이트하려면을 필요로하지 않습니다 if. 숫자를 정렬하고 항상 첫 번째 (최소) 숫자를 사용하십시오.

min=12.45
val=12.55
smallest=$({ echo $min ; echo $val ; } | sort -n | head -n 1)
echo $smallest
min=$smallest

0

일반적으로 파이썬 코드가 포함 된 비슷한 작업을 수행합니다.

#!/bin/sh

min=12.45
val=10.35

python - $min $val<<EOF
if ($min > $val):
        print $min
else: 
        print $val
EOF

-1
$ min=12.45
$ val=10.35
$ [ "$min" \< "$val" ] && echo $val || echo $min
$ 12.45
$ val=13
$ [ "$min" \< "$val" ] && echo $val || echo $min
$ 13

3
답변을
말하고
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.