Tôi muốn so sánh hai số dấu phẩy động trong một tập lệnh shell. Đoạn mã sau không hoạt động:
#!/bin/bash
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo $min
Tôi muốn so sánh hai số dấu phẩy động trong một tập lệnh shell. Đoạn mã sau không hoạt động:
#!/bin/bash
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo $min
Câu trả lời:
Bạn có thể kiểm tra riêng phần nguyên và phần phân số:
#!/bin/bash
min=12.45
val=12.35
if (( ${val%%.*} < ${min%%.*} || ( ${val%%.*} == ${min%%.*} && ${val##*.} < ${min##*.} ) )) ; then
min=$val
fi
echo $min
Như fered nói trong các bình luận, nó chỉ hoạt động nếu cả hai số có các phần phân số và cả hai phần phân số có cùng số chữ số. Đây là phiên bản hoạt động cho số nguyên hoặc phân số và bất kỳ toán tử bash nào:
#!/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
lớn hơn 2
.
Bash không hiểu số học dấu phẩy động. Nó xử lý các số có chứa một dấu thập phân dưới dạng chuỗi.
Sử dụng awk hoặc bc thay thế.
#!/bin/bash
min=12.45
val=10.35
if [ 1 -eq "$(echo "${val} < ${min}" | bc)" ]
then
min=${val}
fi
echo "$min"
Nếu bạn có ý định thực hiện nhiều phép toán, có lẽ tốt hơn là dựa vào python hoặc perl.
Bạn có thể sử dụng gói num-utils cho các thao tác đơn giản ...
Đối với các môn toán nghiêm trọng hơn, hãy xem liên kết này ... Nó mô tả một số tùy chọn, ví dụ.
Một ví dụ của 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.
Đây là một bash
hack ... Nó thêm số 0 đứng đầu vào số nguyên để làm cho một chuỗi so sánh từ trái sang phải có ý nghĩa. Đoạn mã đặc biệt này yêu cầu cả min và val thực sự có dấu thập phân và ít nhất một chữ số thập phân.
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
đầu ra:
min=10.35
Đối với các phép tính đơn giản trên các số dấu phẩy động (+ - * / và so sánh), bạn có thể sử dụng awk.
min=$(echo 12.45 10.35 | awk '{if ($1 < $2) print $1; else print $2}')
Hoặc, nếu bạn có ksh93 hoặc zsh (không phải bash), bạn có thể sử dụng số học tích hợp trong vỏ của mình, hỗ trợ các số dấu phẩy động.
if ((min>val)); then ((val=min)); fi
Để tính toán dấu phẩy động nâng cao hơn, hãy tra cứu bc . Nó thực sự hoạt động trên các số điểm cố định chính xác tùy ý.
Lệnh sort
có một tùy chọn -g
( --general-numeric-sort
) có thể được sử dụng để so sánh trên <
, "nhỏ hơn" hoặc >
"lớn hơn", bằng cách tìm mức tối thiểu hoặc tối đa.
Những ví dụ này đang tìm kiếm mức tối thiểu:
$ printf '12.45\n10.35\n' | sort -g | head -1
10.35
Nó hoạt động với ký hiệu khá chung về số dấu phẩy động, giống như với Ký hiệu điện tử
$ printf '12.45E-10\n10.35\n' | sort -g | head -1
12.45E-10
Lưu ý E-10
, làm cho số đầu tiên 0.000000001245
, thực sự ít hơn 10.35
.
Tiêu chuẩn dấu phẩy động, IEEE754 , xác định một số giá trị đặc biệt. Đối với những so sánh này, những điều thú vị là INF
vô cùng. Ngoài ra còn có vô cực tiêu cực; Cả hai đều được xác định rõ giá trị trong tiêu chuẩn.
$ printf 'INF\n10.35\n' | sort -g | head -1
10.35
$ printf '-INF\n10.35\n' | sort -g | head -1
-INF
Để tìm mức sử dụng tối đa sort -gr
thay vì sort -g
, đảo ngược thứ tự sắp xếp:
$ printf '12.45\n10.35\n' | sort -gr | head -1
12.45
Để thực hiện <
so sánh ("nhỏ hơn"), do đó, nó có thể được sử dụng trong if
vv, so sánh mức tối thiểu với một trong các giá trị. Nếu mức tối thiểu bằng giá trị, được so sánh dưới dạng văn bản , nó nhỏ hơn giá trị khác:
$ 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)
là giống như a <= b
. Điều đáng chú ý là điều này không kiểm tra nghiêm ngặt ít hơn mặc dù. Nếu bạn muốn làm điều đó, bạn cần kiểm tra a == min(a, b) && a != max(a, b)
, bằng cách kháca <= b and not a >= b
Chỉ cần sử dụng ksh
( ksh93
chính xác) hoặc zsh
, cả hai đều hỗ trợ mỹ phẩm điểm nổi:
$ cat test.ksh
#!/bin/ksh
min=12.45
val=10.35
if (( $val < $min )) ; then
min=$val
fi
echo "$min"
$ ./test.ksh
10.35
Chỉnh sửa: Xin lỗi, tôi đã bỏ lỡ ksh93
đã được đề xuất. Giữ câu trả lời của tôi chỉ để làm rõ tập lệnh được đăng trong câu hỏi mở có thể được sử dụng mà không có thay đổi bên ngoài chuyển đổi shell.
Chỉnh sửa2: Lưu ý rằng ksh93
yêu cầu nội dung biến phải phù hợp với ngôn ngữ của bạn, tức là với ngôn ngữ Pháp, phải sử dụng dấu phẩy thay vì dấu chấm:
...
min=12,45
val=10,35
...
Một giải pháp mạnh mẽ hơn là đặt ngôn ngữ ở đầu tập lệnh để đảm bảo nó sẽ hoạt động bất kể ngôn ngữ của người dùng:
...
export LC_ALL=C
min=12.45
val=10.35
...
.
(vì vậy không ở một nửa thế giới có dấu phân cách thập phân ,
). zsh
không có vấn đề đó
LC_ALL
, điều đó cũng có nghĩa là các số sẽ không được hiển thị (hoặc đầu vào) ở định dạng ưa thích của người dùng. Xem unix.stackexchange.com/questions/87745/what-does-lc-all-c-do/ợi để biết cách tiếp cận tốt hơn.
.
dù sao tôi cũng cho rằng dấu phân cách ưa thích của anh ta .
min=$(echo "${min}sa ${val}d la <a p" | dc)
Điều đó sử dụng dc
máy tính để s
xé giá trị $min
trong thanh ghi a
và d
tăng giá trị $val
lên trên đỉnh của ngăn xếp thực thi chính của nó. Sau đó, nó l
đưa nội dung a
lên trên cùng của ngăn xếp, tại điểm đó trông giống như:
${min} ${val} ${val}
Cửa <
sổ bật lên hai mục trên cùng của ngăn xếp và so sánh chúng. Vì vậy, ngăn xếp sau đó trông giống như:
${val}
Nếu mục trên cùng nhỏ hơn mục thứ hai lên trên, nó sẽ đẩy nội dung a
lên trên cùng, vì vậy ngăn xếp trông như sau:
${min} ${val}
Khác nó không làm gì và ngăn xếp vẫn trông như:
${val}
Sau đó, nó chỉ p
gợi ý các mục ngăn xếp hàng đầu.
Vì vậy, đối với vấn đề của bạn:
min=12.45
val=12.35
echo "${min}sa ${val}d la <a p" | dc
###OUTPUT
12.35
Nhưng:
min=12.45
val=12.55
echo "${min}sa ${val}d la <a p" | dc
###OUTPUT
12.45
Tại sao không dùng cũ, tốt expr
?
Cú pháp ví dụ:
if expr 1.09 '>' 1.1 1>/dev/null; then
echo 'not greater'
fi
Đối với các biểu thức đúng , mã thoát expr là 0, với chuỗi '1' được gửi đến thiết bị xuất chuẩn. Đảo ngược cho các biểu thức sai .
Tôi đã kiểm tra điều này với expr GNU và FreeBSD 8.
expr 1.09 '<' -1.1
sẽ in 1
và thoát với 0
(thành công).
Để kiểm tra xem hai số (có thể là phân số) có theo thứ tự hay không, sort
có hợp lý không:
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
Tuy nhiên, nếu bạn thực sự muốn giữ một giá trị tối thiểu được cập nhật, thì bạn không cần if
. Sắp xếp các số và luôn luôn sử dụng số đầu tiên (ít nhất):
min=12.45
val=12.55
smallest=$({ echo $min ; echo $val ; } | sort -n | head -n 1)
echo $smallest
min=$smallest
Thông thường tôi làm những điều tương tự với mã python nhúng:
#!/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
và0.06
). Bạn nên sử dụng một công cụ đã hiểu ký hiệu thập phân.