Saya ingin membandingkan dua angka floating point dalam skrip shell. Kode berikut tidak berfungsi:
#!/bin/bash
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo $min
Saya ingin membandingkan dua angka floating point dalam skrip shell. Kode berikut tidak berfungsi:
#!/bin/bash
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo $min
Jawaban:
Anda dapat memeriksa bagian bilangan bulat dan pecahan secara terpisah:
#!/bin/bash
min=12.45
val=12.35
if (( ${val%%.*} < ${min%%.*} || ( ${val%%.*} == ${min%%.*} && ${val##*.} < ${min##*.} ) )) ; then
min=$val
fi
echo $min
Seperti dikatakan Fered dalam komentar, itu hanya berfungsi jika kedua angka memiliki bagian fraksional dan kedua bagian fraksional memiliki jumlah digit yang sama. Berikut adalah versi yang berfungsi untuk integer atau fraksional dan operator bash apa pun:
#!/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
1.00000000000000000000000001
lebih besar dari 2
.
Bash tidak mengerti aritmatika floating point. Ini memperlakukan angka yang mengandung titik desimal sebagai string.
Gunakan awk atau bc sebagai gantinya.
#!/bin/bash
min=12.45
val=10.35
if [ 1 -eq "$(echo "${val} < ${min}" | bc)" ]
then
min=${val}
fi
echo "$min"
Jika Anda berniat melakukan banyak operasi matematika, mungkin lebih baik mengandalkan python atau perl.
Anda dapat menggunakan paket num-utils untuk manipulasi sederhana ...
Untuk matematika yang lebih serius, lihat tautan ini ... Ini menjelaskan beberapa opsi, misalnya.
Contoh dari 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.
Ini adalah bash
retas ... Itu menambahkan 0's ke integer untuk membuat perbandingan string kiri-ke-kanan bermakna. Potongan kode khusus ini mengharuskan min dan val benar-benar memiliki titik desimal dan setidaknya satu digit desimal.
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
keluaran:
min=10.35
Untuk perhitungan sederhana pada angka floating point (+ - * / dan perbandingan), Anda dapat menggunakan awk.
min=$(echo 12.45 10.35 | awk '{if ($1 < $2) print $1; else print $2}')
Atau, jika Anda memiliki ksh93 atau zsh (bukan bash), Anda dapat menggunakan aritmatika bawaan shell Anda, yang mendukung angka floating point.
if ((min>val)); then ((val=min)); fi
Untuk perhitungan floating point lebih lanjut, cari bc . Ini benar-benar bekerja pada angka fixpoint presisi arbitrer.
Perintah sort
memiliki opsi -g
( --general-numeric-sort
) yang dapat digunakan untuk perbandingan pada <
, "kurang dari" atau>
, "lebih besar dari", dengan menemukan minimum atau maksimum.
Contoh-contoh ini menemukan minimum:
$ printf '12.45\n10.35\n' | sort -g | head -1
10.35
Ini bekerja dengan notasi angka floating point yang cukup umum, seperti dengan E-Notation
$ printf '12.45E-10\n10.35\n' | sort -g | head -1
12.45E-10
Perhatikan E-10
, membuat angka pertama 0.000000001245
, memang kurang dari 10.35
.
Standar floating point, IEEE754 , mendefinisikan beberapa nilai khusus. Untuk perbandingan ini, yang menarik adalah INF
untuk tak terbatas. Ada juga infinity negatif; Keduanya merupakan nilai yang didefinisikan dengan baik dalam standar.
$ printf 'INF\n10.35\n' | sort -g | head -1
10.35
$ printf '-INF\n10.35\n' | sort -g | head -1
-INF
Untuk menemukan penggunaan maksimum sort -gr
sebagai gantinya sort -g
, membalikkan urutan pengurutan:
$ printf '12.45\n10.35\n' | sort -gr | head -1
12.45
Untuk mengimplementasikan <
perbandingan ("kurang dari"), sehingga dapat digunakan dalam if
dll, bandingkan nilai minimum dengan salah satu dari nilai-nilai tersebut. Jika minimum sama dengan nilai, dibandingkan dengan teks , itu lebih kecil dari nilai lainnya:
$ 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)
sama dengan a <= b
. Perlu dicatat bahwa ini tidak memeriksa kurang dari itu. Jika Anda ingin melakukan itu, Anda perlu memeriksa a == min(a, b) && a != max(a, b)
, dengan kata laina <= b and not a >= b
Cukup gunakan ksh
( ksh93
tepatnya) atau zsh
, yang keduanya secara alami mendukung aritmatika floating point:
$ cat test.ksh
#!/bin/ksh
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo "$min"
$ ./test.ksh
10.35
Sunting: Maaf, saya ketinggalan ksh93
sudah disarankan. Menjaga jawaban saya hanya untuk memperjelas skrip yang diposting dalam pertanyaan pembuka dapat digunakan tanpa perubahan di luar saklar shell.
Sunting2: Catatan yang ksh93
mengharuskan konten variabel konsisten dengan lokal Anda, yaitu dengan lokal Perancis, koma bukan titik harus digunakan:
...
min=12,45
val=10,35
...
Solusi yang lebih kuat adalah dengan menetapkan lokal di awal skrip untuk memastikan itu akan berfungsi terlepas dari lokal pengguna:
...
export LC_ALL=C
min=12.45
val=10.35
...
.
(jadi tidak di separuh dunia tempat pemisah desimal berada ,
). zsh
tidak memiliki masalah itu.
LC_ALL
, itu juga berarti bahwa angka tidak akan ditampilkan (atau diinput) dalam format yang disukai pengguna. Lihat unix.stackexchange.com/questions/87745/what-does-lc-all-c-do/… untuk pendekatan yang berpotensi lebih baik.
.
.
min=$(echo "${min}sa ${val}d la <a p" | dc)
Itu menggunakan dc
kalkulator untuk s
merobek nilai $min
dalam register a
dan d
meningkatkan nilai $val
ke atas tumpukan eksekusi utamanya. Kemudian l
ists isinya a
ke atas tumpukan, pada titik yang terlihat seperti:
${min} ${val} ${val}
The <
muncul atas dua entri dari tumpukan dan membandingkan mereka. Jadi tumpukan itu terlihat seperti:
${val}
Jika entri atas kurang dari yang kedua ke atas itu mendorong konten a
ke atas, sehingga tumpukan terlihat seperti:
${min} ${val}
Atau tidak melakukan apa-apa dan tumpukan masih terlihat seperti:
${val}
Kemudian itu hanya p
merusak entri tumpukan atas.
Jadi untuk masalah Anda:
min=12.45
val=12.35
echo "${min}sa ${val}d la <a p" | dc
###OUTPUT
12.35
Tapi:
min=12.45
val=12.55
echo "${min}sa ${val}d la <a p" | dc
###OUTPUT
12.45
Mengapa tidak menggunakan yang lama, bagus expr
?
Sintaks contoh:
if expr 1.09 '>' 1.1 1>/dev/null; then
echo 'not greater'
fi
Untuk ekspresi sebenarnya , kode keluar expr adalah 0, dengan string '1' dikirim ke stdout. Membalikkan untuk yang salah ekspresi .
Saya sudah memeriksa ini dengan GNU dan FreeBSD 8 expr.
expr 1.09 '<' -1.1
akan mencetak 1
dan keluar dengan 0
(sukses).
Untuk memeriksa apakah dua (mungkin pecahan) angka dalam urutan, sort
apakah (cukup) portabel:
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
Namun, jika Anda benar-benar ingin menjaga nilai minimum diperbarui, maka Anda tidak perlu if
. Sortir angkanya, dan selalu gunakan yang pertama (paling tidak):
min=12.45
val=12.55
smallest=$({ echo $min ; echo $val ; } | sort -n | head -n 1)
echo $smallest
min=$smallest
Biasanya saya melakukan hal serupa dengan kode python tertanam:
#!/bin/sh
min=12.45
val=10.35
python - $min $val<<EOF
if ($min > $val):
print $min
else:
print $val
EOF
$ min=12.45
$ val=10.35
$ [ "$min" \< "$val" ] && echo $val || echo $min
$ 12.45
$ val=13
$ [ "$min" \< "$val" ] && echo $val || echo $min
$ 13
0.5
dan0.06
). Anda sebaiknya menggunakan alat yang sudah mengerti notasi desimal.