Bashで2つの浮動小数点数を比較する方法は?


156

私はbashスクリプト内で2つの浮動小数点数を比較しようと頑張っています。私は変数にする必要があります、例えば

let num1=3.17648e-22
let num2=1.5

ここで、これら2つの数値を簡単に比較したいと思います。

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

説明

基本的な電卓コマンドをパイピングすると、bc1または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計算機パッケージがインストールされていることを確認してください。
Serge Stroobandt 2017年

1
0: not found声明を得るif (( $(echo "$TOP_PROCESS_PERCENTAGE > $THRESHOLD" | bc -l) )); then
ステファン

1
「コマンドが見つかりません」と表示されるすべての人にとってbc、バッククォートまたは$()次に(( ))...で囲む必要があることを忘れ(( $(bc -l<<<"$a>$b") ))ないでください(( bc -l<<<"$a>$b" ))
Normadize 2018

@Nemo数字を大文字で科学表記法で記述Eします。構文エラーはすべてなくなります。
Serge 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」記号に必要な間違った計算を回避し、事前に定義された数学ルーチンのためのBCのプログラムの開発に-lフラグを使用する
alrusdi

2
その場合は、非常に類似したソリューションを投稿するだけでなく、重要な違いについて言及せずに、回答でそのことを指摘する必要があります。
Daniel Persson、

4
これは非常に類似したソリューションではありません。Alrusdiのソリューションはbcツールを使用しており、それが私がBASHプログラマーにすすめるものです。BASHはタイプレス言語です。ええ、それは整数演算を行うことができますが、浮動小数点の場合、いくつかの外部ツールを使用する必要があります。それがそれのために作られているので、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スクリプトが大好きですが、浮動小数点はなく、小数点以下2桁もありません(誰かがそのための「偽の」ラッパーを書くことができると思います)。本当に煩わしいです
osirisgothra

2
シェルスクリプトでの使用awkbcシェルスクリプトでの使用は古くからの標準的な手法ですが、awk、bc、その他のUnixツールで使用できるため、シェルに追加されていない機能があると思います。シェルスクリプトを純粋にする必要はありません。
piokuc 2014

1
@WanderingMindこれを行う1つの方法は、Awk exitが適切な機械可読な方法で結果をシェルに返すように、0または1を渡すことです。 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にも...多くのLinux / Unixシステムにデフォルトでインストールphp
anubhava

1
このawkソリューションは、私の場合、bc得られなかった理由で誤った結果を返すものよりも堅牢です。
MBR

22

指数表記なしで浮動小数点数を比較するための純粋なbashソリューション、先行ゼロまたは後続ゼロ:

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

論理演算子の順序は重要です。整数部分は数値として比較され、小数部分は意図的に文字列として比較されます。変数は、この方法を使用して整数部分と小数部分に分割されます。

浮動小数点数と整数(ドットなし)を比較しません。


15

awkをbash if条件と組み合わせて使用​​できます。awkは1または0を出力し、それらはtrueまたはfalseの if句によって解釈されます

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

私はそのようなshell / 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

Debianベースのシステムでdpkg --compare-versionsは、多くの場合に役立ちます。ビルドされたDebianパッケージのバージョンを比較するための完全なロジックがあり、単なるバージョンよりも複雑ですx.y
Neil Mayhew 2017

5

もちろん、本当に浮動小数点演算が必要ない場合、たとえば、小数点以下2桁が常に2桁あるドル値の演算だけの場合は、ドットをドロップして(事実上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への回答としてこれを投稿していたときに、この質問の重複として閉じられたので、ここにも適用されます:

単純さと明快さのために、それは標準の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 2017年

さらに簡潔:not(...)代わりに使用0 if ... else 1
Neil Mayhew

1
あなたがawkとsed(私はCivFanを見ている)を歴史のゴミ箱に追いやっているなら、あなたはお粗末なシステム管理者であり、あまりにも多くのコードを入力しています。(そして私はPythonが好きで使用しているので、それについてではありません)。-1は、誤った場所に配置された迷惑度です。システムドメインには、これらのツールの場所があります。Pythonかどうかは関係ありません。
Mike S

1
おもしろいことに、私はPerlで終わってしまいました!awk '${print $5}' ptpd_log_file | perl -ne '$_ > 0.000100 && print' > /tmp/outfile。かんたん。すべての言語にその場所があります。
Mike S

1
sedsシンタクティックな奇妙さでawkをひとまとめにしないでください。pythonとは異なり、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 2013年

kshがインストールされていないディストリビューションは何ですか?
piokuc 2014

1
@piokuc(Ubuntu Desktop&Serverなど)。それはかなりメジャーなものだと思います...
Olli

また、質問は特にbashで機能するソリューションを求めます。それには本当に理由があるかもしれません。大規模なアプリケーションの一部であり、すべてをkshに移行することは現実的ではありません。または、別のシェルのインストールが本当に問題である組み込みプラットフォームで実行されています。
Olli 2014

1

Javaサポート付きの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のハイブリッド化はさらに多くの機能を提供します...


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 2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.