シェルスクリプトで2つの浮動小数点数を比較したい。次のコードは機能していません:
#!/bin/bash
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo $min
整数部と小数部を別々にチェックできます:
#!/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
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に依存することをお勧めします。
簡単な操作にはパッケージnum-utilsを使用できます...
より深刻な数学については、 このリンク... を参照してください。
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を追加して、文字列の左から右への比較を意味のあるものにします。この特定のコードでは、minとvalの両方に実際に小数点と少なくとも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
浮動小数点数(+-* /および比較)の単純な計算には、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
ksh
(ksh93
正確に)または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
...
min=$(echo "${min}sa ${val}d la <a p" | dc)
これはdc
計算機を使用してs
を$min
の値をレジスターa
に引き継ぎ、d
は$val
の値をメイン実行スタックの最上位。次に、l
istをa
の内容をスタックの一番上に配置します。その時点で、次のようになります。
${min} ${val} ${val}
<
は、スタックから上位2つのエントリをポップし、それらを比較します。したがって、スタックは次のようになります。
${val}
一番上のエントリが2番目に小さい場合、a
の内容が一番上にプッシュされるため、スタックは次のようになります。
${min} ${val}
それ以外の場合は何もせず、スタックは次のようになります。
${val}
それから、それは単に一番上のスタックエントリをp
rintsします。
だからあなたの問題のために:
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
古き良き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で確認しました。
通常、埋め込まれたpython codeを使用して同様のことを行います:
#!/bin/sh
min=12.45
val=10.35
python - $min $val<<EOF
if ($min > $val):
print $min
else:
print $val
EOF
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