シェルスクリプトで浮動小数点数と比較する方法


回答:


5

整数部と小数部を別々に確認できます。

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

feredがコメントで述べているように、両方の数値に小数部があり、両方の小数部に同じ桁数がある場合にのみ機能します。整数または分数と任意の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)。すでに10進表記を理解しているツールを使用することをお勧めします。
ジル 'SO-悪であるのをやめる'

Gillesに感謝します。以前のバージョンよりも一般的に機能するように更新しました。
ATA

それ1.00000000000000000000000001はより大きいと言うことに注意してください2
ステファンシャゼル14

ステファンは正しい。これは、bashの数値表現のビット制限のためです。もちろん、あなたがしたい場合はより多くのあなたが.... :)独自の表現を使用することができます苦しん
アタ

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"

多くの数学演算を行うつもりであれば、おそらくpythonまたはperlに依存する方が良いでしょう。


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の両方 に実際に小数点と少なくとも1つの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

E表記法をサポート

E表記法のように、浮動小数点数のかなり一般的な表記法で動作します

$ 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などで使用できるようにするには、最小値をいずれかの値と比較します。最小値がtextと比較した値と等しい場合、他の値よりも小さくなります。

$ 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)他の言葉でa <= b and not a >= b
デイブ14年

3

kshksh93正確に)またはを使用するだけで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その問題はありません。
ステファンシャゼル14

実際、その点を明確にするために編集された回答。
jlliagre 14

ユーザーがを設定した場合、LC_NUMERICの設定は機能しませんLC_ALL。これは、ユーザーの好みの形式で数値が表示(または入力)されないことも意味します。より良いアプローチの可能性については、unix.stackexchange.com / questions / 87745 / what-does-lc-all-c-do /…を参照してください。
ステファンシャゼル14

@StéphaneChazelasはLC_NUMERICの問題を修正しました。OPスクリプトの構文を考えると、彼の優先セパレーターは.とにかく推測します。
jlliagre

はい。ただし、重要なのはスクリプト作成者のロケールではなく、スクリプトユーザーのロケールです。スクリプトの作成者として、ローカライズとその副作用を考慮する必要があります。
ステファンシャゼル14

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

すなわち、使用dcする計算をsするための値を引き裂いた$minレジスタ内adの値をuplicates $val主実行スタックの上部に。次にl、コンテンツをaスタックの最上部にリストします。この時点で、次のようになります。

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

<スタックから一番上の2つのエントリをポップし、それらを比較します。したがって、スタックは次のようになります。

${val}

上のエントリが上から2番目のエントリよりも小さかった場合、その内容は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(成功)で終了します。
エイドリアンギュンター

0

2つの(おそらく小数の)数値が順番に並んでいるかどうかを確認するに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

通常、私は埋め込まれたPythonコードで同様のことをします:

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