programing

Bash에서 두 부동소수점 숫자를 비교하려면 어떻게 해야 하나요?

muds 2023. 4. 21. 21:33
반응형

Bash에서 두 부동소수점 숫자를 비교하려면 어떻게 해야 하나요?

저는 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

유감스럽게도 num1의 올바른 처리에 문제가 있습니다.이러한 num1은 e포맷일 수 있습니다.

보다 편리하게

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

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

설명.

명령어를 연결bc는 1 0.1의 합니다.

" "-l --mathlib; ; ; ; ; ;의

사이에 (( ))거짓이다

bc기본 계산기 패키지가 설치됩니다.

주의: 지수 표기법은 다음과 같이 기술해야 합니다.*10^ 아니다E,도 아니다e.

예를 들어 다음과 같습니다.

$ echo "1*10^3==1000" |bc
1

반면에.

$ echo "1E3==1000" |bc
0

bc여기에서는 제한에 대해 설명합니다.

는 정수하지만 Bash는 할 수 .bc츠키다

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

지수 기호는 대문자로 해야 합니다.

정수 이외의 수학에는 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

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

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

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

플로트와 정수(점 없음)는 비교하지 않습니다.

다음의 경우, AWK 를 Bash 와 조합해 사용할 수 있습니다.

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

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

를 모두 포함하는 대/소문자 양쪽)을 한 가능한 표기법을 .12.00e4

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

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

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

따라서 두 값의 소수 자릿수가 동일한지 확인해야 합니다.

아래 편집된 코드를 확인하십시오.

#!/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

이거 잘 되네.

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

여기서 나온 답을 사용해서 함수에 넣었어요.다음과 같이 사용할 수 있습니다.

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

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

단순성과 명확성을 위해 AWK를 사용하여 계산하면 됩니다.이것은 표준 Unix 툴이기 때문에 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...

4.5: 구문 오류: 잘못된 산술 연산자(오류 토큰은 ".5"입니다)에 대한 답변으로 이 글을 올렸습니다. 그러나 코드는 여전히 작동하는 것 같습니다. 왜? 이 질문의 중복으로 닫혔을 때, 여기에도 해당됩니다.

스크립트는 설치된 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 an older version of Grails."
else
    echo "Hurray, you have the latest version"
fi

KornShell을 사용합니다.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

사용방법:

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

Java를 지원하는 Bash 돌연변이 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 하이브리드는 더 많은 것을 제공합니다.

매우 심플한 Perl 솔루션:

$ num1=3.2E8
$ num2=2.5E9
$ perl -e "print $num2 > $num1? 'true' : 'false', qq(\n);"
true
$ perl -e "print $num2 < $num1? 'true' : 'false', qq(\n);"
false

이는 perl이 과학적 수치표현의 'E' 표기를 실제로 이해하고 있음을 증명합니다.

$ perl -e "print $num1, qq(\n);"
320000000

셸 스크립트에 'if' 스테이트먼트가 필요한 경우 perl에서 exit 명령을 사용합니다.

$ if perl -e "exit ($num1 > $num2? 0 : 1);"; then echo true; else echo false; fi
false

셸 스크립트에서 0을 반환하는 명령어는 성공하며 if 조건을 통과시킵니다(따라서 if-clause가 실행됩니다).기타 0 이외의 반환값은 장애를 의미합니다.

AWK보다 조금 더 빠르고 필요한 접근법이 하나 있습니다.bc인스톨 합니다.이용하다sort의 플로트 번호 정렬 기능:

A=1280.4
B=9.325
LOW=$(sort -n <<< "$A"$'\n'"$B" | head -1)
if [[ "$LOW" == "$A" ]]; then
    echo "A <= B"
else
    echo "A >= B"
fi

물론 같은 숫자에 대해서는 효과가 없습니다.

를 교환해 주세요.echoprintf 포함(플로트 인식):

st=$(  printf '%50G < %50G\n' "$num1" "$num2" | bc -l  )

원라이너 솔루션

변수가 두 개 있다고 가정합니다.A그리고.B,

echo "($A > $B) * $B + ($A < $B) * $A" | bc

여기 있습니다.gawk+GMP보다 광범위한 잠재적 투입을 고려하는 기반 접근법:

 echo " 5.65e-23 3.14e-22\n
        5.65 3.14e-2203\n
        3.145678 3.145679\n
        3.25353E+9293 325353e9288\n
        3.14159e+200000000001 3.1415899999999999999999E200000000001\n
        100000 100000.0\n
             4096 4096" \
                         \
 | gawk -v PREC=9999999 -nMbe '
  
   NF+=OFS=sprintf(" %s ",
          (+($!_=sprintf("%24s",$!_)<+$NF) \
     ? "<" \
        : (+$NF<+$!_) \
     ? ">" \
        : (int(+$!_)==(__=int(+$NF)))*\
          (__==+$NF)*index($!_,$NF  )  \
     ? "=" \
         : "\342\211\210")' | ecp 
 
                5.65e-23 < 3.14e-22
                    5.65 > 3.14e-2203
                3.145678 < 3.145679
           3.25353E+9293 ≈ 325353e9288
   3.14159e+200000000001 ≈ 3.1415899999999999999999E200000000001
                  100000 ≈ 100000.0
                    4096 = 4096
 

좀 더 명확한 케이스에 대해서는

  • 보다 적은<,
  • 보다 크다>, 또는
  • 와 꼭 같은=(현재로서는 정수 케이스는 제외)

비교적 애매한 경우, 출력합니다.Unicode성격U+2248 ≈ ALMOST EQUAL TO어떤 대가를 치르더라도 해결하려고 하지 않고 말이죠.

대부분의 경우 필요 없습니다.PREC1000만이나 되는 것 같다PREC = 32767일반적인 상황에서 발생하는 대부분의 시나리오에 적합합니다.

언급URL : https://stackoverflow.com/questions/8654051/how-can-i-compare-two-floating-point-numbers-in-bash

반응형