web-dev-qa-db-ja.com

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

シェルスクリプトで2つの浮動小数点数を比較したい。次のコードは機能していません:

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

整数部と小数部を別々にチェックできます:

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

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に依存することをお勧めします。

35
Daniel Näslund

簡単な操作にはパッケージ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
12
Peter.O

浮動小数点数(+-* /および比較)の単純な計算には、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 [〜#〜]example )を検索します。

数値ソートを使用する

コマンドsortには、-g、「より小さい」または--general-numeric-sort、「より大きい」の比較に使用できるオプション<>)があります。 "、最小値または最大値を見つけることによって。

これらの例は最小を見つけています:

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

電子表記をサポート

E-Notationと同様に、浮動小数点数のかなり一般的な表記で機能します。

$ 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
6
Volker Siegel

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
...
3
jlliagre
min=$(echo "${min}sa ${val}d la <a p" | dc)

これはdc計算機を使用してs$minの値をレジスターaに引き継ぎ、d$valの値をメイン実行スタックの最上位。次に、listをaの内容をスタックの一番上に配置します。その時点で、次のようになります。

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

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

${val}

一番上のエントリが2番目に小さい場合、aの内容が一番上にプッシュされるため、スタックは次のようになります。

${min} ${val}

それ以外の場合は何もせず、スタックは次のようになります。

${val} 

それから、それは単に一番上のスタックエントリをprintsします。

だからあなたの問題のために:

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
1
mikeserv

古き良きexprを使ってみませんか?

構文例:

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

true式の場合、expr終了コードは0で、文字列「1」がstdoutに送信されます。 false式の逆。

これをGNUおよびFreeBSD 8 exprで確認しました。

0
sam_pan_mariusz

通常、埋め込まれたpython codeを使用して同様のことを行います:

#!/bin/sh

min=12.45
val=10.35

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

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
David Jones