Bash에서 두 개의 부동 소수점 숫자를 비교하는 방법은 무엇입니까?


156

bash 스크립트 내에서 두 개의 부동 소수점 숫자를 비교하려고 열심히 노력하고 있습니다. 예를 들어 변수를 사용해야합니다

let num1=3.17648e-22
let num2=1.5

이제이 두 숫자를 간단히 비교하고 싶습니다.

st=`echo "$num1 < $num2" | bc`
if [ $st -eq 1]; then
  echo -e "$num1 < $num2"
else
  echo -e "$num1 >= $num2"
fi

불행히도, 나는 "e 형식"일 수있는 num1의 올바른 처리에 약간의 문제가 있습니다. :(

도움, 힌트를 환영합니다!


2
"e 형식"이란 지수 표기법 (과학 표기법이라고도 함)을 의미합니다.
Jonas

답변:


181

더 편리하게

이것은 Bash의 숫자 컨텍스트를 사용하여보다 편리하게 수행 할 수 있습니다.

if (( $(echo "$num1 > $num2" |bc -l) )); then
  
fi

설명

기본 계산기 명령을 통해 파이핑 bc하면 1 또는 0이 반환됩니다.

이 옵션 -l--mathlib; 표준 수학 라이브러리를로드합니다.

큰 괄호 사이에 전체 표현식을 묶으면 (( ))이러한 값이 각각 true 또는 false로 변환됩니다.

bc기본 계산기 패키지가 설치되어 있는지 확인하십시오 .

E예를 들어 대문자 를 사용 하는 경우 과학 형식의 수레에 대해서도 동일하게 작동합니다.num1=3.44E6


1
stackoverflow.com/questions/8654051/ 와 같은 문제 …… $ echo "1.1 + 2e + 02"| bc (standard_in) 1 : 구문 오류
Nemo

1
@MohitArora bc계산기 패키지가 설치되어 있는지 확인하십시오 .
Sroo Stroobandt

1
나는 0: not found진술로을 얻는다 if (( $(echo "$TOP_PROCESS_PERCENTAGE > $THRESHOLD" | bc -l) )); then.
Stephane

1
"명령을 찾을 수 없습니다"얻을 모든 사람들에게, 당신은 묶어야 할 필요가 기억 bc중 하나 역 따옴표 나에 $()에 다음과 (( ))... 즉 (( $(bc -l<<<"$a>$b") ))하지 (( bc -l<<<"$a>$b" )).
Normadize

@Nemo 과학적 표기법으로 숫자를 대문자로 쓰면 E모든 구문 오류가 사라집니다.
Sroo Stroobandt를

100

bash는 정수 수학 만 처리하지만 bc다음과 같이 명령을 사용할 수 있습니다 .

$ num1=3.17648E-22
$ num2=1.5
$ echo $num1'>'$num2 | bc -l
0
$ echo $num2'>'$num1 | bc -l
1

지수 부호는 대문자 여야합니다.


3
예, 그러나 잘못된 계산을 해결하려면 과학적인 숫자 표기법으로 대문자 'e'를 표시하고 사전 정의 된 수학 루틴에 대해 -l 플래그를 bc 프로그램에 사용해야합니다.
alrusdi

2
매우 유사한 솔루션을 게시하고 중요한 차이점을 언급하지 않고 대답에서 지적해야합니다.
Daniel Persson

4
매우 유사한 솔루션 은 아닙니다 . Alrusdi의 솔루션은 bc도구를 사용하므로 BASH 프로그래머에게 권장합니다. BASH는 유형이없는 언어입니다. 예, 정수 산술을 할 수 있지만 부동 소수점에는 외부 도구를 사용해야합니다. 그것이 BC가 만든 것이기 때문에 BC가 최고입니다.
DejanLekic

8
그가 if 문에서 그것을 사용하려고하기 때문에 나는 그것을 보여줄 것입니다. if [$ (... | bc -l) == 1]; 그때 ...
Robert Jacobs

27

awk정수가 아닌 수학 에 사용 하는 것이 좋습니다 . 이 bash 유틸리티 기능을 사용할 수 있습니다 :

numCompare() {
   awk -v n1="$1" -v n2="$2" 'BEGIN {printf "%s " (n1<n2?"<":">=") " %s\n", n1, n2}'
}

그리고 그것을 다음과 같이 부르십시오.

numCompare 5.65 3.14e-22
5.65 >= 3.14e-22

numCompare 5.65e-23 3.14e-22
5.65e-23 < 3.14e-22

numCompare 3.145678 3.145679
3.145678 < 3.145679

2
나는이 답변을 좋아한다, 사람들은 awk esp 초보자로부터 수줍어하는 경향이 있습니다. 그리고 awk는 bc와 마찬가지로 대상 시스템에도 존재한다는 것이 거의 보장됩니다 (어떤 것이 설치되어 있지 않은지 확실하지 않음). 나는 bash 스크립팅을 좋아하지만 부동 소수점은 없으며 소수의 소수 자릿수조차도 아닙니다 (누군가가 그것을 위해 '가짜'래퍼를 쓸 수 있다고 생각합니다), 정말 짜증납니다 ...
osirisgothra

2
고대부터 쉘 스크립트를 사용 awk하고 사용 bc하는 것이 표준 관행입니다. awk, bc 및 기타 유닉스 도구에서 사용할 수 있기 때문에 일부 기능이 쉘에 추가 된 적이 없다고 말할 수 있습니다. 쉘 스크립트에서 순수성을 필요로하지 않습니다.
piokuc

1
@WanderingMind이를 수행하는 한 가지 방법은 0 또는 1을 전달하여 exitAwk가 적절한 기계 판독 가능 방식으로 결과를 쉘에 다시 전달하도록하는 것입니다. if awk -v n1="123.456" -v n2="3.14159e17" 'BEGIN { exit (n1 <= n2) }' /dev/null; then echo bigger; else echo not; fi...하지만 조건이 어떻게 반전되는지 주목하십시오 (종료 상태 0은 쉘 성공을 의미합니다).
tripleee

1
왜 그냥 python. 당신은 perl심지어 .. 많은 리눅스 / 유닉스 시스템에 기본적으로 설치 php
anubhava

1
awk솔루션은 bc내가 얻지 못한 이유로 잘못된 결과를 반환 하는 솔루션보다 내 경우에 더 강력 합니다.
MBR

22

지수 표기법, 선행 또는 후행 0없이 플로트를 비교하기위한 순수 bash 솔루션 :

if [ ${FOO%.*} -eq ${BAR%.*} ] && [ ${FOO#*.} \> ${BAR#*.} ] || [ ${FOO%.*} -gt ${BAR%.*} ]; then
  echo "${FOO} > ${BAR}";
else
  echo "${FOO} <= ${BAR}";
fi

논리 연산자의 순서가 중요 합니다. 정수 부분은 숫자로 비교되고 소수 부분은 의도적으로 문자열로 비교됩니다. 변수는 이 방법을 사용하여 정수 부분과 분수 부분으로 나뉩니다 .

부동 소수점을 정수와 비교하지 않습니다 (점없이).


15

조건, AWK 인쇄 할 경우 당신은 떠들썩한 파티와 AWK 함께 사용할 수있는 1 또는 0을 그 함께하면 절에 의해 해석됩니다 또는 거짓 .

if awk 'BEGIN {print ('$d1' >= '$d2')}'; then
    echo "yes"
else 
    echo "no"
fi

awk를 사용하면 부동 소수점 숫자를 처리 할 수 ​​있기 때문에 훌륭하지만 개인적으로 신택스를 선호합니다if (( $(echo $d1 $d2 | awk '{if ($1 > $2) print 1;}') )); then echo "yes"; else echo "no"; fi
David Georg Reichelt

7

grep 2.20이 버전 2.6보다 큰지 확인하는 것과 같이 패키지 버전 인 숫자를 비교할 때주의하십시오.

$ awk 'BEGIN { print (2.20 >= 2.6) ? "YES" : "NO" }'
NO

$ awk 'BEGIN { print (2.2 >= 2.6) ? "YES" : "NO" }'
NO

$ awk 'BEGIN { print (2.60 == 2.6) ? "YES" : "NO" }'
YES

그런 쉘 / awk 기능으로 이러한 문제를 해결했습니다.

# get version of GNU tool
toolversion() {
    local prog="$1" operator="$2" value="$3" version

    version=$($prog --version | awk '{print $NF; exit}')

    awk -vv1="$version" -vv2="$value" 'BEGIN {
        split(v1, a, /\./); split(v2, b, /\./);
        if (a[1] == b[1]) {
            exit (a[2] '$operator' b[2]) ? 0 : 1
        }
        else {
            exit (a[1] '$operator' b[1]) ? 0 : 1
        }
    }'
}

if toolversion grep '>=' 2.6; then
   # do something awesome
fi

데비안 기반 시스템에서는 dpkg --compare-versions종종 유용합니다. 여기에 내장 된 데비안 패키지 버전을 비교할 수있는 완전한 논리가 있습니다 x.y.
Neil Mayhew

5

물론 실제로 부동 소수점 산술이 필요하지 않은 경우, 항상 정확히 두 개의 십진수가있는 달러 값에 대한 산술은 점을 떨어 뜨리고 (효과적으로 100을 곱한 결과) 정수를 비교할 수 있습니다.

if [[ $((10#${num1/.})) < $((10#${num2/.})) ]]; then
    ...

이를 위해서는 두 값의 소수점 이하 자릿수가 같아야합니다.


3

여기에서 답변을 사용하여 함수에 넣으면 다음과 같이 사용할 수 있습니다.

is_first_floating_number_bigger 1.5 1.2
result="${__FUNCTION_RETURN}"

라고하면 echo $result될 것입니다 1그렇지 않으면,이 경우 0.

함수:

is_first_floating_number_bigger () {
    number1="$1"
    number2="$2"

    [ ${number1%.*} -eq ${number2%.*} ] && [ ${number1#*.} \> ${number2#*.} ] || [ ${number1%.*} -gt ${number2%.*} ];
    result=$?
    if [ "$result" -eq 0 ]; then result=1; else result=0; fi

    __FUNCTION_RETURN="${result}"
}

또는 디버그 출력이있는 버전 :

is_first_floating_number_bigger () {
    number1="$1"
    number2="$2"

    echo "... is_first_floating_number_bigger: comparing ${number1} with ${number2} (to check if the first one is bigger)"

    [ ${number1%.*} -eq ${number2%.*} ] && [ ${number1#*.} \> ${number2#*.} ] || [ ${number1%.*} -gt ${number2%.*} ];
    result=$?
    if [ "$result" -eq 0 ]; then result=1; else result=0; fi

    echo "... is_first_floating_number_bigger: result is: ${result}"

    if [ "$result" -eq 0 ]; then
        echo "... is_first_floating_number_bigger: ${number1} is not bigger than ${number2}"
    else
        echo "... is_first_floating_number_bigger: ${number1} is bigger than ${number2}"
    fi

    __FUNCTION_RETURN="${result}"
}

함수를 분리 된 .sh파일로 저장하고 다음과 같이 포함하십시오.

. /path/to/the/new-file.sh

3

https://stackoverflow.com/a/56415379/1745001에 대한 답변으로 이것을 게시했습니다. 이 질문의 dup으로 닫 혔을 때 여기에도 적용됩니다.

단순성과 명확성을 위해 awk는 표준 UNIX 도구이므로 계산에 awk를 사용하면 bc로 표시되고 구문 적으로 작업하기가 훨씬 쉽습니다.

이 질문에 대해 :

$ cat tst.sh
#!/bin/bash

num1=3.17648e-22
num2=1.5

awk -v num1="$num1" -v num2="$num2" '
BEGIN {
    print "num1", (num1 < num2 ? "<" : ">="), "num2"
}
'

$ ./tst.sh
num1 < num2

그리고이 질문으로 인해 닫힌 다른 질문에 대해 :

$ cat tst.sh
#!/bin/bash

read -p "Operator: " operator
read -p "First number: " ch1
read -p "Second number: " ch2

awk -v ch1="$ch1" -v ch2="$ch2" -v op="$operator" '
BEGIN {
    if ( ( op == "/" ) && ( ch2 == 0 ) ) {
        print "Nope..."
    }
    else {
        print ch1 '"$operator"' ch2
    }
}
'

$ ./tst.sh
Operator: /
First number: 4.5
Second number: 2
2.25

$ ./tst.sh
Operator: /
First number: 4.5
Second number: 0
Nope...

@DudiBoy 아니요, 명확하고 간단하며 이식 가능한 awk 코드 또는 명확하지 않은 모호한 셸 종속 셸 + bc 코드입니다.
Ed Morton

3

awk그것과 같은 도구 (나는 당신을 쳐다보고 있습니다 sed...)는 오래된 프로젝트의 쓰레기통으로 강등되어야합니다. 코드는 결코 읽지 않는 언어로 쓰여지기 때문에 모든 사람이 만지기가 너무 두렵습니다.

또는 코드 유지 관리 최적화보다 CPU 사용량 최적화의 우선 순위를 결정 해야하는 비교적 드문 프로젝트입니다.이 경우 계속하십시오.

그렇지 않다면 왜 대신 읽기 쉽고 명백한 것을 사용하지 python않습니까? 동료 코더와 미래의 자아가 감사합니다. python다른 모든 것과 마찬가지로 bash와 함께 인라인을 사용할 수 있습니다 .

num1=3.17648E-22
num2=1.5
if python -c "exit(0 if $num1 < $num2 else 1)"; then
    echo "yes, $num1 < $num2"
else
    echo "no, $num1 >= $num2"
fi

@Witiko 내 원래 버전은 조금 무섭습니다.
CivFan

더 간결 : not(...)대신 사용0 if ... else 1
Neil Mayhew

1
awk와 sed (CivFan을보고 있습니다)를 역사의 쓰레기통에 강요한다면, 시스템 관리자가 너무 많아서 너무 많은 코드를 입력하고 있습니다. (그리고 나는 파이썬을 좋아하고 사용하므로 그것에 관한 것이 아닙니다.) 잘못 배치 한 경우 -1입니다. 시스템 도메인에는 이러한 도구를위한 장소가 있습니다 (Python 또는 No).
Mike S

1
흥미롭게도, 나는 좋은 ol 'Perl로 끝났다! awk '${print $5}' ptpd_log_file | perl -ne '$_ > 0.000100 && print' > /tmp/outfile. 쉬워요. 모든 언어는 그 자리에 있습니다.
Mike S

1
seds 시냅스 엉망으로 괴로워하지 마십시오. 파이썬과는 달리 awk는 모든 UNIX 설치에서 필수 유틸리티이며 awk python -c "import sys; sys.exit(0 if float($num1) < float($num2) else 1)"는 간단 awk "BEGIN{exit ($num1 > $num2 ? 0 : 1)}"합니다.
Ed Morton

2

이 스크립트는 설치된 grails버전이 필요한 최소 버전보다 큰지 확인하는 데 도움이 될 수 있습니다 . 도움이 되길 바랍니다.

#!/bin/bash                                                                                         

min=1.4                                                                                             
current=`echo $(grails --version | head -n 2 | awk '{print $NF}' | cut -c 1-3)`                         

if [ 1 -eq `echo "${current} < ${min}" | bc` ]                                                          
then                                                                                                
    echo "yo, you have older version of grails."                                                   
else                                                                                                                                                                                                                       
    echo "Hurray, you have the latest version" 
fi

2
num1=0.555
num2=2.555


if [ `echo "$num1>$num2"|bc` -eq 1 ]; then
       echo "$num1 is greater then $num2"
else
       echo "$num2 is greater then $num1"
fi

2

아래 편집 코드를 확인하십시오 :-

#!/bin/bash

export num1=(3.17648*e-22)
export num2=1.5

st=$((`echo "$num1 < $num2"| bc`))
if [ $st -eq 1 ]
  then
    echo -e "$num1 < $num2"
  else
    echo -e "$num1 >= $num2"
fi

이것은 잘 작동합니다.


2

대문자와 소문자 지수가 모두 포함 된 과학적 표기법 (예 :)을 포함한 모든 가능한 표기법을 지원하는 솔루션 12.00e4:

if (( $(bc -l <<< "${value1/e/E} < ${value2/e/E}") ))
then
    echo "$value1 is below $value2"
fi 

1

korn 쉘을 사용하십시오 .bash에서 소수점 부분을 별도로 비교해야 할 수도 있습니다

#!/bin/ksh
X=0.2
Y=0.2
echo $X
echo $Y

if [[ $X -lt $Y ]]
then
     echo "X is less than Y"
elif [[ $X -gt $Y ]]
then
     echo "X is greater than Y"
elif [[ $X -eq $Y ]]
then
     echo "X is equal to Y"
fi

2
문제는 많은 배포판에 ksh가 설치되어 있지 않으며 다른 스크립트에서 스크립트를 사용하려는 경우 특히 bash로 작성 해야하는 스크립트 인 경우 추가 항목을 설치하지 않는 경향이 있다는 것입니다 -다른 쉘이 필요하지 않다고 생각했을 것입니다. 처음에 bash 스크립트를 사용하는 모든 이유가 손상됩니다. 우리가 C ++로 코드를 작성할 수 있는지 확인하십시오.
osirisgothra

ksh가 설치되지 않은 배포판은 무엇입니까?
piokuc

1
@piokuc (예 : Ubuntu Desktop & Server) 나는 그것이 아주 중요한 것이라고 말할 것입니다 ...
Olli

또한 질문은 특히 bash에서 작동하는 솔루션을 요구합니다. 그럴만한 이유가있을 수 있습니다. 큰 응용 프로그램의 일부이며 모든 것을 ksh로 마이그레이션하는 것은 불가능합니다. 또는 다른 쉘을 설치하는 것이 실제로 문제가되는 임베디드 플랫폼에서 실행 중입니다.
Olli

1

자바를 지원하는 bash 돌연변이 인 bashj ( https://sourceforge.net/projects/bashj/ )를 사용하면 다음 과 같이 작성할 수 있습니다 (읽기 쉽습니다).

#!/usr/bin/bashj

#!java
static int doubleCompare(double a,double b) {return((a>b) ? 1 : (a<b) ? -1 : 0);}

#!bashj
num1=3.17648e-22
num2=1.5
comp=j.doubleCompare($num1,$num2)
if [ $comp == 0 ] ; then echo "Equal" ; fi
if [ $comp == 1 ] ; then echo "$num1 > $num2" ; fi
if [ $comp == -1 ] ; then echo "$num2 > $num1" ; fi

물론 bashj bash / java hybridation은 훨씬 더 많은 것을 제공합니다 ...


0

이건 어때요? = D

VAL_TO_CHECK="1.00001"
if [ $(awk '{printf($1 >= $2) ? 1 : 0}' <<<" $VAL_TO_CHECK 1 ") -eq 1 ] ; then
    echo "$VAL_TO_CHECK >= 1"
else
    echo "$VAL_TO_CHECK < 1"
fi

1
Awk 스크립트는 단순히 exit 0진실을보고하고 exit 1거짓을 반환 해야합니다 . 그런 다음 놀랍도록 우아하게 단순화 할 수 있습니다 if awk 'BEGIN { exit (ARGV[1] >= ARGV[2]) ? 0 : 1 }' "$VAL_TO_CHECK" 1; then... (Awk 스크립트를 쉘 함수로 캡슐화하면 더 우아합니다).
tripleee
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.